6226 lines
178 KiB
C
6226 lines
178 KiB
C
/************************************************************
|
|
|
|
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 (c) 2003-2005, Oracle and/or its affiliates. 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, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/** @file events.c
|
|
* 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 <dix-config.h>
|
|
#endif
|
|
|
|
#include <X11/X.h>
|
|
#include "misc.h"
|
|
#include "resource.h"
|
|
#include <X11/Xproto.h>
|
|
#include "windowstr.h"
|
|
#include "inputstr.h"
|
|
#include "inpututils.h"
|
|
#include "scrnintstr.h"
|
|
#include "cursorstr.h"
|
|
|
|
#include "dixstruct.h"
|
|
#ifdef PANORAMIX
|
|
#include "panoramiX.h"
|
|
#include "panoramiXsrv.h"
|
|
#endif
|
|
#include "globals.h"
|
|
|
|
#include <X11/extensions/XKBproto.h>
|
|
#include "xkbsrv.h"
|
|
#include "xace.h"
|
|
|
|
#ifdef XSERVER_DTRACE
|
|
#include <sys/types.h>
|
|
typedef const char *string;
|
|
#include "Xserver-dtrace.h"
|
|
#endif
|
|
|
|
#include <X11/extensions/XIproto.h>
|
|
#include <X11/extensions/XI2proto.h>
|
|
#include <X11/extensions/XI.h>
|
|
#include <X11/extensions/XI2.h>
|
|
#include "exglobals.h"
|
|
#include "exevents.h"
|
|
#include "extnsionst.h"
|
|
|
|
#include "dixevents.h"
|
|
#include "dixgrabs.h"
|
|
#include "dispatch.h"
|
|
|
|
#include <X11/extensions/ge.h>
|
|
#include "geext.h"
|
|
#include "geint.h"
|
|
|
|
#include "eventstr.h"
|
|
#include "enterleave.h"
|
|
#include "eventconvert.h"
|
|
|
|
/* Extension events type numbering starts at EXTENSION_EVENT_BASE. */
|
|
#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 LastEventMask OwnerGrabButtonMask
|
|
#define AllEventMasks (LastEventMask|(LastEventMask-1))
|
|
|
|
|
|
/* @return the core event type or 0 if the event is not a core event */
|
|
static inline int
|
|
core_get_type(const xEvent *event)
|
|
{
|
|
int type = event->u.u.type;
|
|
|
|
return ((type & EXTENSION_EVENT_BASE) || type == GenericEvent) ? 0 : type;
|
|
}
|
|
|
|
/* @return the XI2 event type or 0 if the event is not a XI2 event */
|
|
static inline int
|
|
xi2_get_type(const xEvent *event)
|
|
{
|
|
const xGenericEvent* e = (const xGenericEvent*)event;
|
|
|
|
return (e->type != GenericEvent || e->extension != IReqCode) ? 0 : e->evtype;
|
|
}
|
|
|
|
/**
|
|
* Used to indicate a implicit passive grab created by a ButtonPress event.
|
|
* See DeliverEventsToWindow().
|
|
*/
|
|
#define ImplicitGrabMask (1 << 7)
|
|
|
|
#define WID(w) ((w) ? ((w)->drawable.id) : 0)
|
|
|
|
#define XE_KBPTR (xE->u.keyButtonPointer)
|
|
|
|
|
|
CallbackListPtr EventCallback;
|
|
CallbackListPtr DeviceEventCallback;
|
|
|
|
#define DNPMCOUNT 8
|
|
|
|
Mask DontPropagateMasks[DNPMCOUNT];
|
|
static int DontPropagateRefCnts[DNPMCOUNT];
|
|
|
|
static void CheckVirtualMotion( DeviceIntPtr pDev, QdEventPtr qe, WindowPtr pWin);
|
|
static void CheckPhysLimits(DeviceIntPtr pDev,
|
|
CursorPtr cursor,
|
|
Bool generateEvents,
|
|
Bool confineToScreen,
|
|
ScreenPtr pScreen);
|
|
|
|
/** Key repeat hack. Do not use but in TryClientEvents */
|
|
extern BOOL EventIsKeyRepeat(xEvent *event);
|
|
|
|
/**
|
|
* Main input device struct.
|
|
* inputInfo.pointer
|
|
* is the core pointer. Referred to as "virtual core pointer", "VCP",
|
|
* "core pointer" or inputInfo.pointer. The VCP is the first master
|
|
* pointer device and cannot be deleted.
|
|
*
|
|
* inputInfo.keyboard
|
|
* is the core keyboard ("virtual core keyboard", "VCK", "core keyboard").
|
|
* See inputInfo.pointer.
|
|
*
|
|
* inputInfo.devices
|
|
* linked list containing all devices including VCP and VCK.
|
|
*
|
|
* inputInfo.off_devices
|
|
* Devices that have not been initialized and are thus turned off.
|
|
*
|
|
* inputInfo.numDevices
|
|
* Total number of devices.
|
|
*
|
|
* inputInfo.all_devices
|
|
* Virtual device used for XIAllDevices passive grabs. This device is
|
|
* not part of the inputInfo.devices list and mostly unset except for
|
|
* the deviceid. It exists because passivegrabs need a valid device
|
|
* reference.
|
|
*
|
|
* inputInfo.all_master_devices
|
|
* Virtual device used for XIAllMasterDevices passive grabs. This device
|
|
* is not part of the inputInfo.devices list and mostly unset except for
|
|
* the deviceid. It exists because passivegrabs need a valid device
|
|
* reference.
|
|
*/
|
|
InputInfo inputInfo;
|
|
|
|
EventSyncInfoRec syncEvents;
|
|
|
|
/**
|
|
* The root window the given device is currently on.
|
|
*/
|
|
#define RootWindow(sprite) sprite->spriteTrace[0]
|
|
|
|
static xEvent* swapEvent = NULL;
|
|
static int swapEventLen = 0;
|
|
|
|
void
|
|
NotImplemented(xEvent *from, xEvent *to)
|
|
{
|
|
FatalError("Not implemented");
|
|
}
|
|
|
|
/**
|
|
* Convert the given event type from an XI event to a core event.
|
|
* @param[in] The XI 1.x event type.
|
|
* @return The matching core event type or 0 if there is none.
|
|
*/
|
|
int
|
|
XItoCoreType(int xitype)
|
|
{
|
|
int coretype = 0;
|
|
if (xitype == DeviceMotionNotify)
|
|
coretype = MotionNotify;
|
|
else if (xitype == DeviceButtonPress)
|
|
coretype = ButtonPress;
|
|
else if (xitype == DeviceButtonRelease)
|
|
coretype = ButtonRelease;
|
|
else if (xitype == DeviceKeyPress)
|
|
coretype = KeyPress;
|
|
else if (xitype == DeviceKeyRelease)
|
|
coretype = KeyRelease;
|
|
|
|
return coretype;
|
|
}
|
|
|
|
/**
|
|
* @return true if the device owns a cursor, false if device shares a cursor
|
|
* sprite with another device.
|
|
*/
|
|
Bool
|
|
DevHasCursor(DeviceIntPtr pDev)
|
|
{
|
|
return pDev->spriteInfo->spriteOwner;
|
|
}
|
|
|
|
/*
|
|
* @return true if a device is a pointer, check is the same as used by XI to
|
|
* fill the 'use' field.
|
|
*/
|
|
Bool
|
|
IsPointerDevice(DeviceIntPtr dev)
|
|
{
|
|
return (dev->type == MASTER_POINTER) ||
|
|
(dev->valuator && dev->button) ||
|
|
(dev->valuator && !dev->key);
|
|
}
|
|
|
|
/*
|
|
* @return true if a device is a keyboard, check is the same as used by XI to
|
|
* fill the 'use' field.
|
|
*
|
|
* Some pointer devices have keys as well (e.g. multimedia keys). Try to not
|
|
* count them as keyboard devices.
|
|
*/
|
|
Bool
|
|
IsKeyboardDevice(DeviceIntPtr dev)
|
|
{
|
|
return (dev->type == MASTER_KEYBOARD) ||
|
|
((dev->key && dev->kbdfeed) && !IsPointerDevice(dev));
|
|
}
|
|
|
|
Bool
|
|
IsMaster(DeviceIntPtr dev)
|
|
{
|
|
return dev->type == MASTER_POINTER || dev->type == MASTER_KEYBOARD;
|
|
}
|
|
|
|
Bool
|
|
IsFloating(DeviceIntPtr dev)
|
|
{
|
|
return GetMaster(dev, MASTER_KEYBOARD) == NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* Max event opcode.
|
|
*/
|
|
extern int lastEvent;
|
|
|
|
extern int DeviceMotionNotify;
|
|
|
|
#define CantBeFiltered NoEventMask
|
|
/**
|
|
* Event masks for each event type.
|
|
*
|
|
* One set of filters for each device, initialized by memcpy of
|
|
* default_filter in InitEvents.
|
|
*
|
|
* Filters are used whether a given event may be delivered to a client,
|
|
* usually in the form of if (window-event-mask & filter); then deliver event.
|
|
*
|
|
* One notable filter is for PointerMotion/DevicePointerMotion events. Each
|
|
* time a button is pressed, the filter is modified to also contain the
|
|
* matching ButtonXMotion mask.
|
|
*/
|
|
Mask event_filters[MAXDEVICES][MAXEVENTS];
|
|
|
|
static const Mask default_filter[MAXEVENTS] =
|
|
{
|
|
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 */
|
|
};
|
|
|
|
/**
|
|
* For the given event, return the matching event filter. This filter may then
|
|
* be AND'ed with the selected event mask.
|
|
*
|
|
* For XI2 events, the returned filter is simply the byte containing the event
|
|
* mask we're interested in. E.g. for a mask of (1 << 13), this would be
|
|
* byte[1].
|
|
*
|
|
* @param[in] dev The device the event belongs to, may be NULL.
|
|
* @param[in] event The event to get the filter for. Only the type of the
|
|
* event matters, or the extension + evtype for GenericEvents.
|
|
* @return The filter mask for the given event.
|
|
*
|
|
* @see GetEventMask
|
|
*/
|
|
Mask
|
|
GetEventFilter(DeviceIntPtr dev, xEvent *event)
|
|
{
|
|
int evtype = 0;
|
|
|
|
if (event->u.u.type != GenericEvent)
|
|
return event_get_filter_from_type(dev, event->u.u.type);
|
|
else if ((evtype = xi2_get_type(event)))
|
|
return event_get_filter_from_xi2type(evtype);
|
|
ErrorF("[dix] Unknown event type %d. No filter\n", event->u.u.type);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Return the single byte of the device's XI2 mask that contains the mask
|
|
* for the event_type.
|
|
*/
|
|
int
|
|
GetXI2MaskByte(XI2Mask *mask, DeviceIntPtr dev, int event_type)
|
|
{
|
|
/* we just return the matching filter because that's the only use
|
|
* for this mask anyway.
|
|
*/
|
|
if (xi2mask_isset(mask, dev, event_type))
|
|
return event_get_filter_from_xi2type(event_type);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return TRUE if the mask is set for this event from this device on the
|
|
* window, or FALSE otherwise.
|
|
*/
|
|
Bool
|
|
WindowXI2MaskIsset(DeviceIntPtr dev, WindowPtr win, xEvent* ev)
|
|
{
|
|
OtherInputMasks *inputMasks = wOtherInputMasks(win);
|
|
int evtype;
|
|
|
|
if (!inputMasks || xi2_get_type(ev) == 0)
|
|
return 0;
|
|
|
|
evtype = ((xGenericEvent*)ev)->evtype;
|
|
|
|
return xi2mask_isset(inputMasks->xi2mask, dev, evtype);
|
|
}
|
|
|
|
Mask
|
|
GetEventMask(DeviceIntPtr dev, xEvent *event, InputClients* other)
|
|
{
|
|
int evtype;
|
|
|
|
/* XI2 filters are only ever 8 bit, so let's return a 8 bit mask */
|
|
if ((evtype = xi2_get_type(event)))
|
|
{
|
|
return GetXI2MaskByte(other->xi2mask, dev, evtype);
|
|
} else if (core_get_type(event) != 0)
|
|
return other->mask[XIAllDevices];
|
|
else
|
|
return other->mask[dev->id];
|
|
}
|
|
|
|
|
|
static CARD8 criticalEvents[32] =
|
|
{
|
|
0x7c, 0x30, 0x40 /* key, button, expose, and configure events */
|
|
};
|
|
|
|
static void
|
|
SyntheticMotion(DeviceIntPtr dev, int x, int y) {
|
|
int screenno = 0;
|
|
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
screenno = dev->spriteInfo->sprite->screen->myNum;
|
|
#endif
|
|
PostSyntheticMotion(dev, x, y, screenno,
|
|
(syncEvents.playingEvents) ? syncEvents.time.milliseconds : currentTime.milliseconds);
|
|
|
|
}
|
|
|
|
#ifdef PANORAMIX
|
|
static void PostNewCursor(DeviceIntPtr pDev);
|
|
|
|
static Bool
|
|
XineramaSetCursorPosition(
|
|
DeviceIntPtr pDev,
|
|
int x,
|
|
int y,
|
|
Bool generateEvent
|
|
){
|
|
ScreenPtr pScreen;
|
|
int i;
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
|
|
/* 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 = pSprite->screen;
|
|
x += screenInfo.screens[0]->x;
|
|
y += screenInfo.screens[0]->y;
|
|
|
|
if(!point_on_screen(pScreen, x, y))
|
|
{
|
|
FOR_NSCREENS(i)
|
|
{
|
|
if(i == pScreen->myNum)
|
|
continue;
|
|
if(point_on_screen(screenInfo.screens[i], x, y))
|
|
{
|
|
pScreen = screenInfo.screens[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSprite->screen = pScreen;
|
|
pSprite->hotPhys.x = x - screenInfo.screens[0]->x;
|
|
pSprite->hotPhys.y = y - screenInfo.screens[0]->y;
|
|
x -= pScreen->x;
|
|
y -= pScreen->y;
|
|
|
|
return (*pScreen->SetCursorPosition)(pDev, pScreen, x, y, generateEvent);
|
|
}
|
|
|
|
|
|
static void
|
|
XineramaConstrainCursor(DeviceIntPtr pDev)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
ScreenPtr pScreen;
|
|
BoxRec newBox;
|
|
|
|
pScreen = pSprite->screen;
|
|
newBox = pSprite->physLimits;
|
|
|
|
/* Translate the constraining box to the screen
|
|
the sprite is actually on */
|
|
newBox.x1 += screenInfo.screens[0]->x - pScreen->x;
|
|
newBox.x2 += screenInfo.screens[0]->x - pScreen->x;
|
|
newBox.y1 += screenInfo.screens[0]->y - pScreen->y;
|
|
newBox.y2 += screenInfo.screens[0]->y - pScreen->y;
|
|
|
|
(* pScreen->ConstrainCursor)(pDev, pScreen, &newBox);
|
|
}
|
|
|
|
|
|
static Bool
|
|
XineramaSetWindowPntrs(DeviceIntPtr pDev, WindowPtr pWin)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
|
|
if(pWin == screenInfo.screens[0]->root) {
|
|
int i;
|
|
FOR_NSCREENS(i)
|
|
pSprite->windows[i] = screenInfo.screens[i]->root;
|
|
} else {
|
|
PanoramiXRes *win;
|
|
int rc, i;
|
|
|
|
rc = dixLookupResourceByType((pointer *)&win, pWin->drawable.id,
|
|
XRT_WINDOW, serverClient, DixReadAccess);
|
|
if (rc != Success)
|
|
return FALSE;
|
|
|
|
FOR_NSCREENS(i) {
|
|
rc = dixLookupWindow(pSprite->windows + i, win->info[i].id,
|
|
serverClient, DixReadAccess);
|
|
if (rc != Success) /* window is being unmapped */
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
XineramaConfineCursorToWindow(DeviceIntPtr pDev,
|
|
WindowPtr pWin,
|
|
Bool generateEvents)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
|
|
int x, y, off_x, off_y, i;
|
|
|
|
if(!XineramaSetWindowPntrs(pDev, pWin))
|
|
return;
|
|
|
|
i = PanoramiXNumScreens - 1;
|
|
|
|
RegionCopy(&pSprite->Reg1,
|
|
&pSprite->windows[i]->borderSize);
|
|
off_x = screenInfo.screens[i]->x;
|
|
off_y = screenInfo.screens[i]->y;
|
|
|
|
while(i--) {
|
|
x = off_x - screenInfo.screens[i]->x;
|
|
y = off_y - screenInfo.screens[i]->y;
|
|
|
|
if(x || y)
|
|
RegionTranslate(&pSprite->Reg1, x, y);
|
|
|
|
RegionUnion(&pSprite->Reg1, &pSprite->Reg1,
|
|
&pSprite->windows[i]->borderSize);
|
|
|
|
off_x = screenInfo.screens[i]->x;
|
|
off_y = screenInfo.screens[i]->y;
|
|
}
|
|
|
|
pSprite->hotLimits = *RegionExtents(&pSprite->Reg1);
|
|
|
|
if(RegionNumRects(&pSprite->Reg1) > 1)
|
|
pSprite->hotShape = &pSprite->Reg1;
|
|
else
|
|
pSprite->hotShape = NullRegion;
|
|
|
|
pSprite->confined = FALSE;
|
|
pSprite->confineWin = (pWin == screenInfo.screens[0]->root) ? NullWindow : pWin;
|
|
|
|
CheckPhysLimits(pDev, pSprite->current, generateEvents, FALSE, NULL);
|
|
}
|
|
|
|
#endif /* PANORAMIX */
|
|
|
|
/**
|
|
* Modifies the filter for the given protocol event type to the given masks.
|
|
*
|
|
* There's only two callers: UpdateDeviceState() and XI's SetMaskForExtEvent().
|
|
* The latter initialises masks for the matching XI events, it's a once-off
|
|
* setting.
|
|
* UDS however changes the mask for MotionNotify and DeviceMotionNotify each
|
|
* time a button is pressed to include the matching ButtonXMotion mask in the
|
|
* filter.
|
|
*
|
|
* @param[in] deviceid The device to modify the filter for.
|
|
* @param[in] mask The new filter mask.
|
|
* @param[in] event Protocol event type.
|
|
*/
|
|
void
|
|
SetMaskForEvent(int deviceid, Mask mask, int event)
|
|
{
|
|
if (deviceid < 0 || deviceid >= MAXDEVICES)
|
|
FatalError("SetMaskForEvent: bogus device id");
|
|
event_filters[deviceid][event] = mask;
|
|
}
|
|
|
|
void
|
|
SetCriticalEvent(int event)
|
|
{
|
|
if (event >= MAXEVENTS)
|
|
FatalError("SetCriticalEvent: bogus event number");
|
|
criticalEvents[event >> 3] |= 1 << (event & 7);
|
|
}
|
|
|
|
void
|
|
ConfineToShape(DeviceIntPtr pDev, RegionPtr shape, int *px, int *py)
|
|
{
|
|
BoxRec box;
|
|
int x = *px, y = *py;
|
|
int incx = 1, incy = 1;
|
|
|
|
if (RegionContainsPoint(shape, x, y, &box))
|
|
return;
|
|
box = *RegionExtents(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 (!RegionContainsPoint(shape, x, y, &box));
|
|
*px = x;
|
|
*py = y;
|
|
}
|
|
|
|
static void
|
|
CheckPhysLimits(
|
|
DeviceIntPtr pDev,
|
|
CursorPtr cursor,
|
|
Bool generateEvents,
|
|
Bool confineToScreen, /* unused if PanoramiX on */
|
|
ScreenPtr pScreen) /* unused if PanoramiX on */
|
|
{
|
|
HotSpot new;
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
|
|
if (!cursor)
|
|
return;
|
|
new = pSprite->hotPhys;
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
/* I don't care what the DDX has to say about it */
|
|
pSprite->physLimits = pSprite->hotLimits;
|
|
else
|
|
#endif
|
|
{
|
|
if (pScreen)
|
|
new.pScreen = pScreen;
|
|
else
|
|
pScreen = new.pScreen;
|
|
(*pScreen->CursorLimits) (pDev, pScreen, cursor, &pSprite->hotLimits,
|
|
&pSprite->physLimits);
|
|
pSprite->confined = confineToScreen;
|
|
(* pScreen->ConstrainCursor)(pDev, pScreen, &pSprite->physLimits);
|
|
}
|
|
|
|
/* constrain the pointer to those limits */
|
|
if (new.x < pSprite->physLimits.x1)
|
|
new.x = pSprite->physLimits.x1;
|
|
else
|
|
if (new.x >= pSprite->physLimits.x2)
|
|
new.x = pSprite->physLimits.x2 - 1;
|
|
if (new.y < pSprite->physLimits.y1)
|
|
new.y = pSprite->physLimits.y1;
|
|
else
|
|
if (new.y >= pSprite->physLimits.y2)
|
|
new.y = pSprite->physLimits.y2 - 1;
|
|
if (pSprite->hotShape)
|
|
ConfineToShape(pDev, pSprite->hotShape, &new.x, &new.y);
|
|
if ((
|
|
#ifdef PANORAMIX
|
|
noPanoramiXExtension &&
|
|
#endif
|
|
(pScreen != pSprite->hotPhys.pScreen)) ||
|
|
(new.x != pSprite->hotPhys.x) || (new.y != pSprite->hotPhys.y))
|
|
{
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
XineramaSetCursorPosition (pDev, new.x, new.y, generateEvents);
|
|
else
|
|
#endif
|
|
{
|
|
if (pScreen != pSprite->hotPhys.pScreen)
|
|
pSprite->hotPhys = new;
|
|
(*pScreen->SetCursorPosition)
|
|
(pDev, pScreen, new.x, new.y, generateEvents);
|
|
}
|
|
if (!generateEvents)
|
|
SyntheticMotion(pDev, new.x, new.y);
|
|
}
|
|
|
|
#ifdef PANORAMIX
|
|
/* Tell DDX what the limits are */
|
|
if (!noPanoramiXExtension)
|
|
XineramaConstrainCursor(pDev);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
CheckVirtualMotion(
|
|
DeviceIntPtr pDev,
|
|
QdEventPtr qe,
|
|
WindowPtr pWin)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
RegionPtr reg = NULL;
|
|
DeviceEvent *ev = NULL;
|
|
|
|
if (qe)
|
|
{
|
|
ev = &qe->event->device_event;
|
|
switch(ev->type)
|
|
{
|
|
case ET_Motion:
|
|
case ET_ButtonPress:
|
|
case ET_ButtonRelease:
|
|
case ET_KeyPress:
|
|
case ET_KeyRelease:
|
|
case ET_ProximityIn:
|
|
case ET_ProximityOut:
|
|
pSprite->hot.pScreen = qe->pScreen;
|
|
pSprite->hot.x = ev->root_x;
|
|
pSprite->hot.y = ev->root_y;
|
|
pWin = pDev->deviceGrab.grab ? pDev->deviceGrab.grab->confineTo : NullWindow;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (pWin)
|
|
{
|
|
BoxRec lims;
|
|
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension) {
|
|
int x, y, off_x, off_y, i;
|
|
|
|
if(!XineramaSetWindowPntrs(pDev, pWin))
|
|
return;
|
|
|
|
i = PanoramiXNumScreens - 1;
|
|
|
|
RegionCopy(&pSprite->Reg2,
|
|
&pSprite->windows[i]->borderSize);
|
|
off_x = screenInfo.screens[i]->x;
|
|
off_y = screenInfo.screens[i]->y;
|
|
|
|
while(i--) {
|
|
x = off_x - screenInfo.screens[i]->x;
|
|
y = off_y - screenInfo.screens[i]->y;
|
|
|
|
if(x || y)
|
|
RegionTranslate(&pSprite->Reg2, x, y);
|
|
|
|
RegionUnion(&pSprite->Reg2, &pSprite->Reg2,
|
|
&pSprite->windows[i]->borderSize);
|
|
|
|
off_x = screenInfo.screens[i]->x;
|
|
off_y = screenInfo.screens[i]->y;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (pSprite->hot.pScreen != pWin->drawable.pScreen)
|
|
{
|
|
pSprite->hot.pScreen = pWin->drawable.pScreen;
|
|
pSprite->hot.x = pSprite->hot.y = 0;
|
|
}
|
|
}
|
|
|
|
lims = *RegionExtents(&pWin->borderSize);
|
|
if (pSprite->hot.x < lims.x1)
|
|
pSprite->hot.x = lims.x1;
|
|
else if (pSprite->hot.x >= lims.x2)
|
|
pSprite->hot.x = lims.x2 - 1;
|
|
if (pSprite->hot.y < lims.y1)
|
|
pSprite->hot.y = lims.y1;
|
|
else if (pSprite->hot.y >= lims.y2)
|
|
pSprite->hot.y = lims.y2 - 1;
|
|
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
{
|
|
if (RegionNumRects(&pSprite->Reg2) > 1)
|
|
reg = &pSprite->Reg2;
|
|
|
|
} else
|
|
#endif
|
|
{
|
|
if (wBoundingShape(pWin))
|
|
reg = &pWin->borderSize;
|
|
}
|
|
|
|
if (reg)
|
|
ConfineToShape(pDev, reg, &pSprite->hot.x, &pSprite->hot.y);
|
|
|
|
if (qe && ev)
|
|
{
|
|
qe->pScreen = pSprite->hot.pScreen;
|
|
ev->root_x = pSprite->hot.x;
|
|
ev->root_y = pSprite->hot.y;
|
|
}
|
|
}
|
|
#ifdef PANORAMIX
|
|
if (noPanoramiXExtension) /* No typo. Only set the root win if disabled */
|
|
#endif
|
|
RootWindow(pDev->spriteInfo->sprite) = pSprite->hot.pScreen->root;
|
|
}
|
|
|
|
static void
|
|
ConfineCursorToWindow(DeviceIntPtr pDev, WindowPtr pWin, Bool generateEvents, Bool confineToScreen)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
|
|
if (syncEvents.playingEvents)
|
|
{
|
|
CheckVirtualMotion(pDev, (QdEventPtr)NULL, pWin);
|
|
SyntheticMotion(pDev, pSprite->hot.x, pSprite->hot.y);
|
|
}
|
|
else
|
|
{
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension) {
|
|
XineramaConfineCursorToWindow(pDev, pWin, generateEvents);
|
|
return;
|
|
}
|
|
#endif
|
|
pSprite->hotLimits = *RegionExtents(&pWin->borderSize);
|
|
pSprite->hotShape = wBoundingShape(pWin) ? &pWin->borderSize
|
|
: NullRegion;
|
|
CheckPhysLimits(pDev, pSprite->current, generateEvents,
|
|
confineToScreen, pWin->drawable.pScreen);
|
|
}
|
|
}
|
|
|
|
Bool
|
|
PointerConfinedToScreen(DeviceIntPtr pDev)
|
|
{
|
|
return pDev->spriteInfo->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(DeviceIntPtr pDev, CursorPtr cursor)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
ScreenPtr pScreen;
|
|
|
|
if (cursor != pSprite->current)
|
|
{
|
|
if ((pSprite->current->bits->xhot != cursor->bits->xhot) ||
|
|
(pSprite->current->bits->yhot != cursor->bits->yhot))
|
|
CheckPhysLimits(pDev, cursor, FALSE, pSprite->confined,
|
|
(ScreenPtr)NULL);
|
|
#ifdef PANORAMIX
|
|
/* XXX: is this really necessary?? (whot) */
|
|
if (!noPanoramiXExtension)
|
|
pScreen = pSprite->screen;
|
|
else
|
|
#endif
|
|
pScreen = pSprite->hotPhys.pScreen;
|
|
|
|
(*pScreen->DisplayCursor)(pDev, pScreen, cursor);
|
|
FreeCursor(pSprite->current, (Cursor)0);
|
|
pSprite->current = cursor;
|
|
pSprite->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(DeviceIntPtr pDev)
|
|
{
|
|
WindowPtr win;
|
|
GrabPtr grab = pDev->deviceGrab.grab;
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
CursorPtr pCursor;
|
|
|
|
if (syncEvents.playingEvents)
|
|
return;
|
|
if (grab)
|
|
{
|
|
if (grab->cursor)
|
|
{
|
|
ChangeToCursor(pDev, grab->cursor);
|
|
return;
|
|
}
|
|
if (IsParent(grab->window, pSprite->win))
|
|
win = pSprite->win;
|
|
else
|
|
win = grab->window;
|
|
}
|
|
else
|
|
win = pSprite->win;
|
|
for (; win; win = win->parent)
|
|
{
|
|
if (win->optional)
|
|
{
|
|
pCursor = WindowGetDeviceCursor(win, pDev);
|
|
if (!pCursor && win->optional->cursor != NullCursor)
|
|
pCursor = win->optional->cursor;
|
|
if (pCursor)
|
|
{
|
|
ChangeToCursor(pDev, pCursor);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param dev device which you want to know its current root window
|
|
* @return root window where dev's sprite is located
|
|
*/
|
|
WindowPtr
|
|
GetCurrentRootWindow(DeviceIntPtr dev)
|
|
{
|
|
return RootWindow(dev->spriteInfo->sprite);
|
|
}
|
|
|
|
/**
|
|
* @return window underneath the cursor sprite.
|
|
*/
|
|
WindowPtr
|
|
GetSpriteWindow(DeviceIntPtr pDev)
|
|
{
|
|
return pDev->spriteInfo->sprite->win;
|
|
}
|
|
|
|
/**
|
|
* @return current sprite cursor.
|
|
*/
|
|
CursorPtr
|
|
GetSpriteCursor(DeviceIntPtr pDev)
|
|
{
|
|
return pDev->spriteInfo->sprite->current;
|
|
}
|
|
|
|
/**
|
|
* Set x/y current sprite position in screen coordinates.
|
|
*/
|
|
void
|
|
GetSpritePosition(DeviceIntPtr pDev, int *px, int *py)
|
|
{
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
*px = pSprite->hotPhys.x;
|
|
*py = pSprite->hotPhys.y;
|
|
}
|
|
|
|
#ifdef PANORAMIX
|
|
int
|
|
XineramaGetCursorScreen(DeviceIntPtr pDev)
|
|
{
|
|
if(!noPanoramiXExtension) {
|
|
return pDev->spriteInfo->sprite->screen->myNum;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
#endif /* PANORAMIX */
|
|
|
|
#define TIMESLOP (5 * 60 * 1000) /* 5 minutes */
|
|
|
|
static void
|
|
MonthChangedOrBadTime(InternalEvent *ev)
|
|
{
|
|
/* 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 - ev->any.time) > TIMESLOP)
|
|
currentTime.months++;
|
|
else
|
|
ev->any.time = currentTime.milliseconds;
|
|
}
|
|
|
|
static void
|
|
NoticeTime(InternalEvent *ev)
|
|
{
|
|
if (ev->any.time < currentTime.milliseconds)
|
|
MonthChangedOrBadTime(ev);
|
|
currentTime.milliseconds = ev->any.time;
|
|
lastDeviceEventTime = currentTime;
|
|
}
|
|
|
|
void
|
|
NoticeEventTime(InternalEvent *ev)
|
|
{
|
|
if (!syncEvents.playingEvents)
|
|
NoticeTime(ev);
|
|
}
|
|
|
|
/**************************************************************************
|
|
* The following procedures deal with synchronous events *
|
|
**************************************************************************/
|
|
|
|
/**
|
|
* EnqueueEvent is a device's processInputProc if a device is frozen.
|
|
* Instead of delivering the events to the client, the event is tacked onto a
|
|
* linked list for later delivery.
|
|
*/
|
|
void
|
|
EnqueueEvent(InternalEvent *ev, DeviceIntPtr device)
|
|
{
|
|
QdEventPtr tail;
|
|
QdEventPtr qe;
|
|
SpritePtr pSprite = device->spriteInfo->sprite;
|
|
int eventlen;
|
|
DeviceEvent *event = &ev->device_event;
|
|
|
|
tail = list_last_entry(&syncEvents.pending, QdEventRec, next);
|
|
|
|
NoticeTime((InternalEvent*)event);
|
|
|
|
/* Fix for key repeating bug. */
|
|
if (device->key != NULL && device->key->xkbInfo != NULL &&
|
|
event->type == ET_KeyRelease)
|
|
AccessXCancelRepeatKey(device->key->xkbInfo, event->detail.key);
|
|
|
|
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 (ev->any.type == ET_Motion)
|
|
ev->device_event.root = pSprite->hotPhys.pScreen->root->drawable.id;
|
|
|
|
eventinfo.event = ev;
|
|
eventinfo.device = device;
|
|
CallCallbacks(&DeviceEventCallback, (pointer)&eventinfo);
|
|
}
|
|
|
|
if (event->type == ET_Motion)
|
|
{
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension) {
|
|
event->root_x += pSprite->screen->x - screenInfo.screens[0]->x;
|
|
event->root_y += pSprite->screen->y - screenInfo.screens[0]->y;
|
|
}
|
|
#endif
|
|
pSprite->hotPhys.x = event->root_x;
|
|
pSprite->hotPhys.y = event->root_y;
|
|
/* do motion compression, but not if from different devices */
|
|
if (tail &&
|
|
(tail->event->any.type == ET_Motion) &&
|
|
(tail->device == device) &&
|
|
(tail->pScreen == pSprite->hotPhys.pScreen))
|
|
{
|
|
DeviceEvent *tailev = &tail->event->device_event;
|
|
tailev->root_x = pSprite->hotPhys.x;
|
|
tailev->root_y = pSprite->hotPhys.y;
|
|
tailev->time = event->time;
|
|
tail->months = currentTime.months;
|
|
return;
|
|
}
|
|
}
|
|
|
|
eventlen = event->length;
|
|
|
|
qe = malloc(sizeof(QdEventRec) + eventlen);
|
|
if (!qe)
|
|
return;
|
|
list_init(&qe->next);
|
|
qe->device = device;
|
|
qe->pScreen = pSprite->hotPhys.pScreen;
|
|
qe->months = currentTime.months;
|
|
qe->event = (InternalEvent *)(qe + 1);
|
|
memcpy(qe->event, event, eventlen);
|
|
list_append(&qe->next, &syncEvents.pending);
|
|
}
|
|
|
|
/**
|
|
* Run through the list of events queued up in syncEvents.
|
|
* For each event do:
|
|
* If the device for this event is not frozen anymore, take it and process it
|
|
* as usually.
|
|
* After that, check if there's any devices in the list that are not frozen.
|
|
* If there is none, we're done. If there is at least one device that is not
|
|
* frozen, then re-run from the beginning of the event queue.
|
|
*/
|
|
void
|
|
PlayReleasedEvents(void)
|
|
{
|
|
QdEventPtr tmp;
|
|
QdEventPtr qe;
|
|
DeviceIntPtr dev;
|
|
DeviceIntPtr pDev;
|
|
|
|
restart:
|
|
list_for_each_entry_safe(qe, tmp, &syncEvents.pending, next) {
|
|
if (!qe->device->deviceGrab.sync.frozen)
|
|
{
|
|
list_del(&qe->next);
|
|
pDev = qe->device;
|
|
if (qe->event->any.type == ET_Motion)
|
|
CheckVirtualMotion(pDev, qe, NullWindow);
|
|
syncEvents.time.months = qe->months;
|
|
syncEvents.time.milliseconds = qe->event->any.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) {
|
|
DeviceEvent *ev = &qe->event->device_event;
|
|
switch(ev->type)
|
|
{
|
|
case ET_Motion:
|
|
case ET_ButtonPress:
|
|
case ET_ButtonRelease:
|
|
case ET_KeyPress:
|
|
case ET_KeyRelease:
|
|
case ET_ProximityIn:
|
|
case ET_ProximityOut:
|
|
ev->root_x += screenInfo.screens[0]->x -
|
|
pDev->spriteInfo->sprite->screen->x;
|
|
ev->root_y += screenInfo.screens[0]->y -
|
|
pDev->spriteInfo->sprite->screen->y;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
(*qe->device->public.processInputProc)(qe->event, qe->device);
|
|
free(qe);
|
|
for (dev = inputInfo.devices; dev && dev->deviceGrab.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 */
|
|
goto restart;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Freeze or thaw the given devices. The device's processing proc is
|
|
* switched to either the real processing proc (in case of thawing) or an
|
|
* enqueuing processing proc (usually EnqueueEvent()).
|
|
*
|
|
* @param dev The device to freeze/thaw
|
|
* @param frozen True to freeze or false to thaw.
|
|
*/
|
|
static void
|
|
FreezeThaw(DeviceIntPtr dev, Bool frozen)
|
|
{
|
|
dev->deviceGrab.sync.frozen = frozen;
|
|
if (frozen)
|
|
dev->public.processInputProc = dev->public.enqueueInputProc;
|
|
else
|
|
dev->public.processInputProc = dev->public.realInputProc;
|
|
}
|
|
|
|
/**
|
|
* Unfreeze devices and replay all events to the respective clients.
|
|
*
|
|
* ComputeFreezes takes the first event in the device's frozen event queue. It
|
|
* runs up the sprite tree (spriteTrace) and searches for the window to replay
|
|
* the events from. If it is found, it checks for passive grabs one down from
|
|
* the window or delivers the events.
|
|
*/
|
|
static void
|
|
ComputeFreezes(void)
|
|
{
|
|
DeviceIntPtr replayDev = syncEvents.replayDev;
|
|
WindowPtr w;
|
|
GrabPtr grab;
|
|
DeviceIntPtr dev;
|
|
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
FreezeThaw(dev, dev->deviceGrab.sync.other ||
|
|
(dev->deviceGrab.sync.state >= FROZEN));
|
|
if (syncEvents.playingEvents ||
|
|
(!replayDev && list_is_empty(&syncEvents.pending)))
|
|
return;
|
|
syncEvents.playingEvents = TRUE;
|
|
if (replayDev)
|
|
{
|
|
DeviceEvent* event = replayDev->deviceGrab.sync.event;
|
|
|
|
syncEvents.replayDev = (DeviceIntPtr)NULL;
|
|
|
|
w = XYToWindow(replayDev->spriteInfo->sprite,
|
|
event->root_x, event->root_y);
|
|
if (!CheckDeviceGrabs(replayDev, event, syncEvents.replayWin))
|
|
{
|
|
if (replayDev->focus && !IsPointerEvent((InternalEvent*)event))
|
|
DeliverFocusedEvent(replayDev, (InternalEvent*)event, w);
|
|
else
|
|
DeliverDeviceEvents(w, (InternalEvent*)event, NullGrab,
|
|
NullWindow, replayDev);
|
|
}
|
|
}
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
if (!dev->deviceGrab.sync.frozen)
|
|
{
|
|
PlayReleasedEvents();
|
|
break;
|
|
}
|
|
}
|
|
syncEvents.playingEvents = FALSE;
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
if (DevHasCursor(dev))
|
|
{
|
|
/* the following may have been skipped during replay,
|
|
so do it now */
|
|
if ((grab = dev->deviceGrab.grab) && grab->confineTo)
|
|
{
|
|
if (grab->confineTo->drawable.pScreen !=
|
|
dev->spriteInfo->sprite->hotPhys.pScreen)
|
|
dev->spriteInfo->sprite->hotPhys.x =
|
|
dev->spriteInfo->sprite->hotPhys.y = 0;
|
|
ConfineCursorToWindow(dev, grab->confineTo, TRUE, TRUE);
|
|
}
|
|
else
|
|
ConfineCursorToWindow(dev,
|
|
dev->spriteInfo->sprite->hotPhys.pScreen->root,
|
|
TRUE, FALSE);
|
|
PostNewCursor(dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef RANDR
|
|
void
|
|
ScreenRestructured (ScreenPtr pScreen)
|
|
{
|
|
GrabPtr grab;
|
|
DeviceIntPtr pDev;
|
|
|
|
for (pDev = inputInfo.devices; pDev; pDev = pDev->next)
|
|
{
|
|
if (!DevHasCursor(pDev))
|
|
continue;
|
|
|
|
/* GrabDevice doesn't have a confineTo field, so we don't need to
|
|
* worry about it. */
|
|
if ((grab = pDev->deviceGrab.grab) && grab->confineTo)
|
|
{
|
|
if (grab->confineTo->drawable.pScreen
|
|
!= pDev->spriteInfo->sprite->hotPhys.pScreen)
|
|
pDev->spriteInfo->sprite->hotPhys.x = pDev->spriteInfo->sprite->hotPhys.y = 0;
|
|
ConfineCursorToWindow(pDev, grab->confineTo, TRUE, TRUE);
|
|
}
|
|
else
|
|
ConfineCursorToWindow(pDev,
|
|
pDev->spriteInfo->sprite->hotPhys.pScreen->root,
|
|
TRUE, FALSE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
CheckGrabForSyncs(DeviceIntPtr thisDev, Bool thisMode, Bool otherMode)
|
|
{
|
|
GrabPtr grab = thisDev->deviceGrab.grab;
|
|
DeviceIntPtr dev;
|
|
|
|
if (thisMode == GrabModeSync)
|
|
thisDev->deviceGrab.sync.state = FROZEN_NO_EVENT;
|
|
else
|
|
{ /* free both if same client owns both */
|
|
thisDev->deviceGrab.sync.state = THAWED;
|
|
if (thisDev->deviceGrab.sync.other &&
|
|
(CLIENT_BITS(thisDev->deviceGrab.sync.other->resource) ==
|
|
CLIENT_BITS(grab->resource)))
|
|
thisDev->deviceGrab.sync.other = NullGrab;
|
|
}
|
|
|
|
if (IsMaster(thisDev))
|
|
{
|
|
dev = GetPairedDevice(thisDev);
|
|
if (otherMode == GrabModeSync)
|
|
dev->deviceGrab.sync.other = grab;
|
|
else
|
|
{ /* free both if same client owns both */
|
|
if (dev->deviceGrab.sync.other &&
|
|
(CLIENT_BITS(dev->deviceGrab.sync.other->resource) ==
|
|
CLIENT_BITS(grab->resource)))
|
|
dev->deviceGrab.sync.other = NullGrab;
|
|
}
|
|
}
|
|
ComputeFreezes();
|
|
}
|
|
|
|
/**
|
|
* Save the device's master device id. This needs to be done
|
|
* if a client directly grabs a slave device that is attached to a master. For
|
|
* the duration of the grab, the device is detached, ungrabbing re-attaches it
|
|
* though.
|
|
*
|
|
* We store the ID of the master device only in case the master disappears
|
|
* while the device has a grab.
|
|
*/
|
|
static void
|
|
DetachFromMaster(DeviceIntPtr dev)
|
|
{
|
|
if (IsFloating(dev))
|
|
return;
|
|
|
|
dev->saved_master_id = GetMaster(dev, MASTER_ATTACHED)->id;
|
|
|
|
AttachDevice(NULL, dev, NULL);
|
|
}
|
|
|
|
static void
|
|
ReattachToOldMaster(DeviceIntPtr dev)
|
|
{
|
|
DeviceIntPtr master = NULL;
|
|
|
|
if (IsMaster(dev))
|
|
return;
|
|
|
|
dixLookupDevice(&master, dev->saved_master_id, serverClient, DixUseAccess);
|
|
|
|
if (master)
|
|
{
|
|
AttachDevice(serverClient, dev, master);
|
|
dev->saved_master_id = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Activate a pointer grab on the given device. A pointer grab will cause all
|
|
* core pointer events of this device to be delivered to the grabbing client only.
|
|
* No other device will send core events to the grab client while the grab is
|
|
* on, but core events will be sent to other clients.
|
|
* Can cause the cursor to change if a grab cursor is set.
|
|
*
|
|
* Note that parameter autoGrab may be (True & ImplicitGrabMask) if the grab
|
|
* is an implicit grab caused by a ButtonPress event.
|
|
*
|
|
* @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)
|
|
{
|
|
GrabInfoPtr grabinfo = &mouse->deviceGrab;
|
|
WindowPtr oldWin = (grabinfo->grab) ?
|
|
grabinfo->grab->window
|
|
: mouse->spriteInfo->sprite->win;
|
|
Bool isPassive = autoGrab & ~ImplicitGrabMask;
|
|
|
|
/* slave devices need to float for the duration of the grab. */
|
|
if (grab->grabtype == XI2 &&
|
|
!(autoGrab & ImplicitGrabMask) && !IsMaster(mouse))
|
|
DetachFromMaster(mouse);
|
|
|
|
if (grab->confineTo)
|
|
{
|
|
if (grab->confineTo->drawable.pScreen
|
|
!= mouse->spriteInfo->sprite->hotPhys.pScreen)
|
|
mouse->spriteInfo->sprite->hotPhys.x =
|
|
mouse->spriteInfo->sprite->hotPhys.y = 0;
|
|
ConfineCursorToWindow(mouse, grab->confineTo, FALSE, TRUE);
|
|
}
|
|
DoEnterLeaveEvents(mouse, mouse->id, oldWin, grab->window, NotifyGrab);
|
|
mouse->valuator->motionHintWindow = NullWindow;
|
|
if (syncEvents.playingEvents)
|
|
grabinfo->grabTime = syncEvents.time;
|
|
else
|
|
grabinfo->grabTime = time;
|
|
if (grab->cursor)
|
|
grab->cursor->refcnt++;
|
|
CopyGrab(grabinfo->activeGrab, grab);
|
|
grabinfo->grab = grabinfo->activeGrab;
|
|
grabinfo->fromPassiveGrab = isPassive;
|
|
grabinfo->implicitGrab = autoGrab & ImplicitGrabMask;
|
|
PostNewCursor(mouse);
|
|
CheckGrabForSyncs(mouse,(Bool)grab->pointerMode, (Bool)grab->keyboardMode);
|
|
}
|
|
|
|
/**
|
|
* Delete grab on given device, update the sprite.
|
|
*
|
|
* Extension devices are set up for ActivateKeyboardGrab().
|
|
*/
|
|
void
|
|
DeactivatePointerGrab(DeviceIntPtr mouse)
|
|
{
|
|
GrabPtr grab = mouse->deviceGrab.grab;
|
|
DeviceIntPtr dev;
|
|
Bool wasImplicit = (mouse->deviceGrab.fromPassiveGrab &&
|
|
mouse->deviceGrab.implicitGrab);
|
|
|
|
mouse->valuator->motionHintWindow = NullWindow;
|
|
mouse->deviceGrab.grab = NullGrab;
|
|
mouse->deviceGrab.sync.state = NOT_GRABBED;
|
|
mouse->deviceGrab.fromPassiveGrab = FALSE;
|
|
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
if (dev->deviceGrab.sync.other == grab)
|
|
dev->deviceGrab.sync.other = NullGrab;
|
|
}
|
|
DoEnterLeaveEvents(mouse, mouse->id, grab->window,
|
|
mouse->spriteInfo->sprite->win, NotifyUngrab);
|
|
if (grab->confineTo)
|
|
ConfineCursorToWindow(mouse, GetCurrentRootWindow(mouse), FALSE, FALSE);
|
|
PostNewCursor(mouse);
|
|
if (grab->cursor)
|
|
FreeCursor(grab->cursor, (Cursor)0);
|
|
|
|
if (!wasImplicit && grab->grabtype == XI2)
|
|
ReattachToOldMaster(mouse);
|
|
|
|
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)
|
|
{
|
|
GrabInfoPtr grabinfo = &keybd->deviceGrab;
|
|
WindowPtr oldWin;
|
|
|
|
/* slave devices need to float for the duration of the grab. */
|
|
if (grab->grabtype == XI2 &&
|
|
!(passive & ImplicitGrabMask) &&
|
|
!IsMaster(keybd))
|
|
DetachFromMaster(keybd);
|
|
|
|
if (grabinfo->grab)
|
|
oldWin = grabinfo->grab->window;
|
|
else if (keybd->focus)
|
|
oldWin = keybd->focus->win;
|
|
else
|
|
oldWin = keybd->spriteInfo->sprite->win;
|
|
if (oldWin == FollowKeyboardWin)
|
|
oldWin = keybd->focus->win;
|
|
if (keybd->valuator)
|
|
keybd->valuator->motionHintWindow = NullWindow;
|
|
DoFocusEvents(keybd, oldWin, grab->window, NotifyGrab);
|
|
if (syncEvents.playingEvents)
|
|
grabinfo->grabTime = syncEvents.time;
|
|
else
|
|
grabinfo->grabTime = time;
|
|
CopyGrab(grabinfo->activeGrab, grab);
|
|
grabinfo->grab = grabinfo->activeGrab;
|
|
grabinfo->fromPassiveGrab = passive;
|
|
grabinfo->implicitGrab = passive & ImplicitGrabMask;
|
|
CheckGrabForSyncs(keybd, (Bool)grab->keyboardMode, (Bool)grab->pointerMode);
|
|
}
|
|
|
|
/**
|
|
* Delete keyboard grab for the given device.
|
|
*/
|
|
void
|
|
DeactivateKeyboardGrab(DeviceIntPtr keybd)
|
|
{
|
|
GrabPtr grab = keybd->deviceGrab.grab;
|
|
DeviceIntPtr dev;
|
|
WindowPtr focusWin = keybd->focus ? keybd->focus->win
|
|
: keybd->spriteInfo->sprite->win;
|
|
Bool wasImplicit = (keybd->deviceGrab.fromPassiveGrab &&
|
|
keybd->deviceGrab.implicitGrab);
|
|
|
|
if (focusWin == FollowKeyboardWin)
|
|
focusWin = inputInfo.keyboard->focus->win;
|
|
if (keybd->valuator)
|
|
keybd->valuator->motionHintWindow = NullWindow;
|
|
keybd->deviceGrab.grab = NullGrab;
|
|
keybd->deviceGrab.sync.state = NOT_GRABBED;
|
|
keybd->deviceGrab.fromPassiveGrab = FALSE;
|
|
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
if (dev->deviceGrab.sync.other == grab)
|
|
dev->deviceGrab.sync.other = NullGrab;
|
|
}
|
|
DoFocusEvents(keybd, grab->window, focusWin, NotifyUngrab);
|
|
|
|
if (!wasImplicit && grab->grabtype == XI2)
|
|
ReattachToOldMaster(keybd);
|
|
|
|
ComputeFreezes();
|
|
}
|
|
|
|
void
|
|
AllowSome(ClientPtr client,
|
|
TimeStamp time,
|
|
DeviceIntPtr thisDev,
|
|
int newState)
|
|
{
|
|
Bool thisGrabbed, otherGrabbed, othersFrozen, thisSynced;
|
|
TimeStamp grabTime;
|
|
DeviceIntPtr dev;
|
|
GrabInfoPtr devgrabinfo,
|
|
grabinfo = &thisDev->deviceGrab;
|
|
|
|
thisGrabbed = grabinfo->grab && SameClient(grabinfo->grab, client);
|
|
thisSynced = FALSE;
|
|
otherGrabbed = FALSE;
|
|
othersFrozen = FALSE;
|
|
grabTime = grabinfo->grabTime;
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
devgrabinfo = &dev->deviceGrab;
|
|
|
|
if (dev == thisDev)
|
|
continue;
|
|
if (devgrabinfo->grab && SameClient(devgrabinfo->grab, client))
|
|
{
|
|
if (!(thisGrabbed || otherGrabbed) ||
|
|
(CompareTimeStamps(devgrabinfo->grabTime, grabTime) == LATER))
|
|
grabTime = devgrabinfo->grabTime;
|
|
otherGrabbed = TRUE;
|
|
if (grabinfo->sync.other == devgrabinfo->grab)
|
|
thisSynced = TRUE;
|
|
if (devgrabinfo->sync.state >= FROZEN)
|
|
othersFrozen = TRUE;
|
|
}
|
|
}
|
|
if (!((thisGrabbed && grabinfo->sync.state >= FROZEN) || thisSynced))
|
|
return;
|
|
if ((CompareTimeStamps(time, currentTime) == LATER) ||
|
|
(CompareTimeStamps(time, grabTime) == EARLIER))
|
|
return;
|
|
switch (newState)
|
|
{
|
|
case THAWED: /* Async */
|
|
if (thisGrabbed)
|
|
grabinfo->sync.state = THAWED;
|
|
if (thisSynced)
|
|
grabinfo->sync.other = NullGrab;
|
|
ComputeFreezes();
|
|
break;
|
|
case FREEZE_NEXT_EVENT: /* Sync */
|
|
if (thisGrabbed)
|
|
{
|
|
grabinfo->sync.state = FREEZE_NEXT_EVENT;
|
|
if (thisSynced)
|
|
grabinfo->sync.other = NullGrab;
|
|
ComputeFreezes();
|
|
}
|
|
break;
|
|
case THAWED_BOTH: /* AsyncBoth */
|
|
if (othersFrozen)
|
|
{
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
devgrabinfo = &dev->deviceGrab;
|
|
if (devgrabinfo->grab
|
|
&& SameClient(devgrabinfo->grab, client))
|
|
devgrabinfo->sync.state = THAWED;
|
|
if (devgrabinfo->sync.other &&
|
|
SameClient(devgrabinfo->sync.other, client))
|
|
devgrabinfo->sync.other = NullGrab;
|
|
}
|
|
ComputeFreezes();
|
|
}
|
|
break;
|
|
case FREEZE_BOTH_NEXT_EVENT: /* SyncBoth */
|
|
if (othersFrozen)
|
|
{
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
devgrabinfo = &dev->deviceGrab;
|
|
if (devgrabinfo->grab
|
|
&& SameClient(devgrabinfo->grab, client))
|
|
devgrabinfo->sync.state = FREEZE_BOTH_NEXT_EVENT;
|
|
if (devgrabinfo->sync.other
|
|
&& SameClient(devgrabinfo->sync.other, client))
|
|
devgrabinfo->sync.other = NullGrab;
|
|
}
|
|
ComputeFreezes();
|
|
}
|
|
break;
|
|
case NOT_GRABBED: /* Replay */
|
|
if (thisGrabbed && grabinfo->sync.state == FROZEN_WITH_EVENT)
|
|
{
|
|
if (thisSynced)
|
|
grabinfo->sync.other = NullGrab;
|
|
syncEvents.replayDev = thisDev;
|
|
syncEvents.replayWin = grabinfo->grab->window;
|
|
(*grabinfo->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;
|
|
devgrabinfo = &dev->deviceGrab;
|
|
if (devgrabinfo->grab
|
|
&& SameClient(devgrabinfo->grab, client))
|
|
devgrabinfo->sync.state = THAWED;
|
|
if (devgrabinfo->sync.other
|
|
&& SameClient(devgrabinfo->sync.other, client))
|
|
devgrabinfo->sync.other = NullGrab;
|
|
}
|
|
ComputeFreezes();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for AllowEvents request.
|
|
*
|
|
* Release some events from a frozen device.
|
|
*/
|
|
int
|
|
ProcAllowEvents(ClientPtr client)
|
|
{
|
|
TimeStamp time;
|
|
DeviceIntPtr mouse = NULL;
|
|
DeviceIntPtr keybd = NULL;
|
|
REQUEST(xAllowEventsReq);
|
|
|
|
REQUEST_SIZE_MATCH(xAllowEventsReq);
|
|
time = ClientTimeToServerTime(stuff->time);
|
|
|
|
mouse = PickPointer(client);
|
|
keybd = PickKeyboard(client);
|
|
|
|
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->deviceGrab.grab && SameClient(dev->deviceGrab.grab, client))
|
|
{
|
|
(*dev->deviceGrab.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 dev The device the event came from. May be NULL.
|
|
* @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.
|
|
*/
|
|
int
|
|
TryClientEvents (ClientPtr client, DeviceIntPtr dev, xEvent *pEvents,
|
|
int count, Mask mask, Mask filter, GrabPtr grab)
|
|
{
|
|
int type;
|
|
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF("[dix] Event([%d, %d], mask=0x%lx), client=%d%s",
|
|
pEvents->u.u.type, pEvents->u.u.detail, mask,
|
|
client ? client->index : -1,
|
|
(client && client->clientGone) ? " (gone)" : "");
|
|
#endif
|
|
|
|
if (!client || client == serverClient || client->clientGone) {
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" not delivered to fake/dead client\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (filter != CantBeFiltered && !(mask & filter))
|
|
{
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" filtered\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (grab && !SameClient(grab, client))
|
|
{
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" not delivered due to grab\n");
|
|
#endif
|
|
return -1; /* don't send, but notify caller */
|
|
}
|
|
|
|
type = pEvents->u.u.type;
|
|
if (type == MotionNotify)
|
|
{
|
|
if (mask & PointerMotionHintMask)
|
|
{
|
|
if (WID(dev->valuator->motionHintWindow) ==
|
|
pEvents->u.keyButtonPointer.event)
|
|
{
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF("[dix] \n");
|
|
ErrorF("[dix] 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;
|
|
}
|
|
}
|
|
else if (type == DeviceMotionNotify)
|
|
{
|
|
if (MaybeSendDeviceMotionNotifyHint((deviceKeyButtonPointer*)pEvents,
|
|
mask) != 0)
|
|
return 1;
|
|
} else if (type == KeyPress)
|
|
{
|
|
if (EventIsKeyRepeat(pEvents))
|
|
{
|
|
if (!_XkbWantsDetectableAutoRepeat(client))
|
|
{
|
|
xEvent release = *pEvents;
|
|
release.u.u.type = KeyRelease;
|
|
WriteEventsToClient(client, 1, &release);
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" (plus fake core release for repeat)");
|
|
#endif
|
|
} else
|
|
{
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" (detectable autorepeat for core)");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} else if (type == DeviceKeyPress)
|
|
{
|
|
if (EventIsKeyRepeat(pEvents))
|
|
{
|
|
if (!_XkbWantsDetectableAutoRepeat(client))
|
|
{
|
|
deviceKeyButtonPointer release = *(deviceKeyButtonPointer *)pEvents;
|
|
release.type = DeviceKeyRelease;
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" (plus fake xi1 release for repeat)");
|
|
#endif
|
|
WriteEventsToClient(client, 1, (xEvent *) &release);
|
|
}
|
|
else {
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF(" (detectable autorepeat for core)");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BitIsOn(criticalEvents, type))
|
|
{
|
|
if (client->smart_priority < SMART_MAX_PRIORITY)
|
|
client->smart_priority++;
|
|
SetCriticalOutputPending();
|
|
}
|
|
|
|
WriteEventsToClient(client, count, pEvents);
|
|
#ifdef DEBUG_EVENTS
|
|
ErrorF("[dix] delivered\n");
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static BOOL
|
|
ActivateImplicitGrab(DeviceIntPtr dev, ClientPtr client, WindowPtr win,
|
|
xEvent *event, Mask deliveryMask)
|
|
{
|
|
GrabPtr tempGrab;
|
|
OtherInputMasks *inputMasks;
|
|
CARD8 type = event->u.u.type;
|
|
enum InputLevel grabtype;
|
|
|
|
if (type == ButtonPress)
|
|
grabtype = CORE;
|
|
else if (type == DeviceButtonPress)
|
|
grabtype = XI;
|
|
else if ((type = xi2_get_type(event)) == XI_ButtonPress)
|
|
grabtype = XI2;
|
|
else
|
|
return FALSE;
|
|
|
|
tempGrab = AllocGrab();
|
|
if (!tempGrab)
|
|
return FALSE;
|
|
tempGrab->next = NULL;
|
|
tempGrab->device = dev;
|
|
tempGrab->resource = client->clientAsMask;
|
|
tempGrab->window = win;
|
|
tempGrab->ownerEvents = (deliveryMask & OwnerGrabButtonMask) ? TRUE : FALSE;
|
|
tempGrab->eventMask = deliveryMask;
|
|
tempGrab->keyboardMode = GrabModeAsync;
|
|
tempGrab->pointerMode = GrabModeAsync;
|
|
tempGrab->confineTo = NullWindow;
|
|
tempGrab->cursor = NullCursor;
|
|
tempGrab->type = type;
|
|
tempGrab->grabtype = grabtype;
|
|
|
|
/* get the XI and XI2 device mask */
|
|
inputMasks = wOtherInputMasks(win);
|
|
tempGrab->deviceMask = (inputMasks) ? inputMasks->inputEvents[dev->id]: 0;
|
|
|
|
if (inputMasks)
|
|
xi2mask_merge(tempGrab->xi2mask, inputMasks->xi2mask);
|
|
|
|
(*dev->deviceGrab.ActivateGrab)(dev, tempGrab,
|
|
currentTime, TRUE | ImplicitGrabMask);
|
|
FreeGrab(tempGrab);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Attempt event delivery to the client owning the window.
|
|
*/
|
|
static enum EventDeliveryState
|
|
DeliverToWindowOwner(DeviceIntPtr dev, WindowPtr win,
|
|
xEvent *events, int count, Mask filter,
|
|
GrabPtr grab)
|
|
{
|
|
/* if nobody ever wants to see this event, skip some work */
|
|
if (filter != CantBeFiltered &&
|
|
!((wOtherEventMasks(win)|win->eventMask) & filter))
|
|
return EVENT_SKIP;
|
|
|
|
if (IsInterferingGrab(wClient(win), dev, events))
|
|
return EVENT_SKIP;
|
|
|
|
if (!XaceHook(XACE_RECEIVE_ACCESS, wClient(win), win, events, count))
|
|
{
|
|
int attempt = TryClientEvents(wClient(win), dev, events,
|
|
count, win->eventMask,
|
|
filter, grab);
|
|
if (attempt > 0)
|
|
return EVENT_DELIVERED;
|
|
if (attempt < 0)
|
|
return EVENT_REJECTED;
|
|
}
|
|
|
|
return EVENT_NOT_DELIVERED;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the list of clients that should be tried for event delivery on the
|
|
* given window.
|
|
*
|
|
* @return 1 if the client list should be traversed, zero if the event
|
|
* should be skipped.
|
|
*/
|
|
static Bool
|
|
GetClientsForDelivery(DeviceIntPtr dev, WindowPtr win,
|
|
xEvent *events, Mask filter, InputClients **clients)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (core_get_type(events) != 0)
|
|
*clients = (InputClients *)wOtherClients(win);
|
|
else if (xi2_get_type(events) != 0)
|
|
{
|
|
OtherInputMasks *inputMasks = wOtherInputMasks(win);
|
|
/* Has any client selected for the event? */
|
|
if (!WindowXI2MaskIsset(dev, win, events))
|
|
goto out;
|
|
*clients = inputMasks->inputClients;
|
|
} else {
|
|
OtherInputMasks *inputMasks = wOtherInputMasks(win);
|
|
/* Has any client selected for the event? */
|
|
if (!inputMasks ||
|
|
!(inputMasks->inputEvents[dev->id] & filter))
|
|
goto out;
|
|
|
|
*clients = inputMasks->inputClients;
|
|
}
|
|
|
|
rc = 1;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Try delivery on each client in inputclients, provided the event mask
|
|
* accepts it and there is no interfering core grab..
|
|
*/
|
|
static enum EventDeliveryState
|
|
DeliverEventToInputClients(DeviceIntPtr dev, InputClients *inputclients,
|
|
WindowPtr win, xEvent *events,
|
|
int count, Mask filter, GrabPtr grab,
|
|
ClientPtr *client_return, Mask *mask_return)
|
|
{
|
|
int attempt;
|
|
enum EventDeliveryState rc = EVENT_NOT_DELIVERED;
|
|
|
|
for (; inputclients; inputclients = inputclients->next)
|
|
{
|
|
Mask mask;
|
|
ClientPtr client = rClient(inputclients);
|
|
|
|
if (IsInterferingGrab(client, dev, events))
|
|
continue;
|
|
|
|
mask = GetEventMask(dev, events, inputclients);
|
|
|
|
if (XaceHook(XACE_RECEIVE_ACCESS, client, win,
|
|
events, count))
|
|
/* do nothing */;
|
|
else if ( (attempt = TryClientEvents(client, dev,
|
|
events, count,
|
|
mask, filter, grab)) )
|
|
{
|
|
if (attempt > 0)
|
|
{
|
|
rc = EVENT_DELIVERED;
|
|
*client_return = client;
|
|
*mask_return = mask;
|
|
/* Success overrides non-success, so if we've been
|
|
* successful on one client, return that */
|
|
} else if (rc == EVENT_NOT_DELIVERED)
|
|
rc = EVENT_REJECTED;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Deliver events to clients registered on the window.
|
|
*
|
|
* @param client_return On successful delivery, set to the recipient.
|
|
* @param mask_return On successful delivery, set to the recipient's event
|
|
* mask for this event.
|
|
*/
|
|
static enum EventDeliveryState
|
|
DeliverEventToWindowMask(DeviceIntPtr dev, WindowPtr win, xEvent *events,
|
|
int count, Mask filter, GrabPtr grab,
|
|
ClientPtr *client_return, Mask *mask_return)
|
|
{
|
|
InputClients *clients;
|
|
|
|
if (!GetClientsForDelivery(dev, win, events, filter, &clients))
|
|
return EVENT_SKIP;
|
|
|
|
return DeliverEventToInputClients(dev, clients, win, events, count, filter,
|
|
grab, client_return, mask_return);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Core events are always delivered to the window owner. If the filter is
|
|
* something other than CantBeFiltered, the event is also delivered to other
|
|
* clients with the matching mask on the window.
|
|
*
|
|
* 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.
|
|
*
|
|
* @return a positive number if at least one successful delivery has been
|
|
* made, 0 if no events were delivered, or a negative number if the event
|
|
* has not been delivered _and_ rejected by at least one client.
|
|
*/
|
|
int
|
|
DeliverEventsToWindow(DeviceIntPtr pDev, WindowPtr pWin, xEvent
|
|
*pEvents, int count, Mask filter, GrabPtr grab)
|
|
{
|
|
int deliveries = 0, nondeliveries = 0;
|
|
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;
|
|
|
|
/* Deliver to window owner */
|
|
if ((filter == CantBeFiltered) || core_get_type(pEvents) != 0)
|
|
{
|
|
enum EventDeliveryState rc;
|
|
|
|
rc = DeliverToWindowOwner(pDev, pWin, pEvents, count, filter, grab);
|
|
|
|
switch(rc)
|
|
{
|
|
case EVENT_SKIP:
|
|
return 0;
|
|
case EVENT_REJECTED:
|
|
nondeliveries--;
|
|
break;
|
|
case EVENT_DELIVERED:
|
|
/* We delivered to the owner, with our event mask */
|
|
deliveries++;
|
|
client = wClient(pWin);
|
|
deliveryMask = pWin->eventMask;
|
|
break;
|
|
case EVENT_NOT_DELIVERED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* CantBeFiltered means only window owner gets the event */
|
|
if (filter != CantBeFiltered)
|
|
{
|
|
enum EventDeliveryState rc;
|
|
|
|
rc = DeliverEventToWindowMask(pDev, pWin, pEvents, count, filter,
|
|
grab, &client, &deliveryMask);
|
|
|
|
switch(rc)
|
|
{
|
|
case EVENT_SKIP:
|
|
return 0;
|
|
case EVENT_REJECTED:
|
|
nondeliveries--;
|
|
break;
|
|
case EVENT_DELIVERED:
|
|
deliveries++;
|
|
break;
|
|
case EVENT_NOT_DELIVERED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (deliveries)
|
|
{
|
|
/*
|
|
* Note that since core events are delivered first, an implicit grab may
|
|
* be activated on a core grab, stopping the XI events.
|
|
*/
|
|
if (!grab && ActivateImplicitGrab(pDev, client, pWin, pEvents, deliveryMask))
|
|
/* grab activated */;
|
|
else if (type == MotionNotify)
|
|
pDev->valuator->motionHintWindow = pWin;
|
|
else if (type == DeviceMotionNotify || type == DeviceButtonPress)
|
|
CheckDeviceGrabAndHintWindow (pWin, type,
|
|
(deviceKeyButtonPointer*) pEvents,
|
|
grab, client, deliveryMask);
|
|
return deliveries;
|
|
}
|
|
return nondeliveries;
|
|
}
|
|
|
|
/**
|
|
* Filter out raw events for XI 2.0 and XI 2.1 clients.
|
|
*
|
|
* If there is a grab on the device, 2.0 clients only get raw events if they
|
|
* have the grab. 2.1+ clients get raw events in all cases.
|
|
*
|
|
* @return TRUE if the event should be discarded, FALSE otherwise.
|
|
*/
|
|
static BOOL
|
|
FilterRawEvents(const ClientPtr client, const GrabPtr grab)
|
|
{
|
|
XIClientPtr client_xi_version;
|
|
int cmp;
|
|
|
|
/* device not grabbed -> don't filter */
|
|
if (!grab)
|
|
return FALSE;
|
|
|
|
client_xi_version = dixLookupPrivate(&client->devPrivates, XIClientPrivateKey);
|
|
|
|
cmp = version_compare(client_xi_version->major_version,
|
|
client_xi_version->minor_version, 2, 0);
|
|
/* XI 2.0: if device is grabbed, skip
|
|
XI 2.1: if device is grabbed by us, skip, we've already delivered */
|
|
return (cmp == 0) ? TRUE : SameClient(grab, client);
|
|
}
|
|
|
|
/**
|
|
* Deliver a raw event to the grab owner (if any) and to all root windows.
|
|
*
|
|
* Raw event delivery differs between XI 2.0 and XI 2.1.
|
|
* XI 2.0: events delivered to the grabbing client (if any) OR to all root
|
|
* windows
|
|
* XI 2.1: events delivered to all root windows, regardless of grabbing
|
|
* state.
|
|
*/
|
|
void
|
|
DeliverRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
|
|
{
|
|
GrabPtr grab = device->deviceGrab.grab;
|
|
xEvent *xi;
|
|
int i, rc;
|
|
int filter;
|
|
|
|
rc = EventToXI2((InternalEvent*)ev, (xEvent**)&xi);
|
|
if (rc != Success)
|
|
{
|
|
ErrorF("[Xi] %s: XI2 conversion failed in %s (%d)\n",
|
|
__func__, device->name, rc);
|
|
return;
|
|
}
|
|
|
|
if (grab)
|
|
DeliverGrabbedEvent((InternalEvent*)ev, device, FALSE);
|
|
|
|
filter = GetEventFilter(device, xi);
|
|
|
|
for (i = 0; i < screenInfo.numScreens; i++)
|
|
{
|
|
WindowPtr root;
|
|
InputClients *inputclients;
|
|
|
|
root = screenInfo.screens[i]->root;
|
|
if (!GetClientsForDelivery(device, root, xi, filter, &inputclients))
|
|
continue;
|
|
|
|
for (; inputclients; inputclients = inputclients->next)
|
|
{
|
|
ClientPtr c; /* unused */
|
|
Mask m; /* unused */
|
|
InputClients ic = *inputclients;
|
|
|
|
/* Because we run through the list manually, copy the actual
|
|
* list, shorten the copy to only have one client and then pass
|
|
* that down to DeliverEventToInputClients. This way we avoid
|
|
* double events on XI 2.1 clients that have a grab on the
|
|
* device.
|
|
*/
|
|
ic.next = NULL;
|
|
|
|
if (!FilterRawEvents(rClient(&ic), grab))
|
|
DeliverEventToInputClients(device, &ic, root, xi, 1,
|
|
filter, NULL, &c, &m);
|
|
}
|
|
}
|
|
|
|
free(xi);
|
|
}
|
|
|
|
/* 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
|
|
if (XaceHook(XACE_RECEIVE_ACCESS, wClient(pWin), pWin, pEvents, count))
|
|
return 1; /* don't send, but pretend we did */
|
|
return TryClientEvents(wClient(pWin), NULL, 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
|
|
if (XaceHook(XACE_RECEIVE_ACCESS, rClient(other), pWin, pEvents,
|
|
count))
|
|
return 1; /* don't send, but pretend we did */
|
|
return TryClientEvents(rClient(other), NULL, pEvents, count,
|
|
other->mask, filter, NullGrab);
|
|
}
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
static Window FindChildForEvent(SpritePtr pSprite, WindowPtr event)
|
|
{
|
|
WindowPtr w = DeepestSpriteWin(pSprite);
|
|
Window child = None;
|
|
|
|
/* 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 == event)
|
|
{
|
|
child = None;
|
|
break;
|
|
}
|
|
|
|
if (w->parent == event)
|
|
{
|
|
child = w->drawable.id;
|
|
break;
|
|
}
|
|
w = w->parent;
|
|
}
|
|
return child;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void
|
|
FixUpEventFromWindow(
|
|
SpritePtr pSprite,
|
|
xEvent *xE,
|
|
WindowPtr pWin,
|
|
Window child,
|
|
Bool calcChild)
|
|
{
|
|
int evtype;
|
|
|
|
if (calcChild)
|
|
child = FindChildForEvent(pSprite, pWin);
|
|
|
|
if ((evtype = xi2_get_type(xE)))
|
|
{
|
|
xXIDeviceEvent* event = (xXIDeviceEvent*)xE;
|
|
|
|
switch (evtype)
|
|
{
|
|
case XI_RawKeyPress:
|
|
case XI_RawKeyRelease:
|
|
case XI_RawButtonPress:
|
|
case XI_RawButtonRelease:
|
|
case XI_RawMotion:
|
|
case XI_DeviceChanged:
|
|
case XI_HierarchyChanged:
|
|
case XI_PropertyEvent:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
event->root = RootWindow(pSprite)->drawable.id;
|
|
event->event = pWin->drawable.id;
|
|
if (pSprite->hot.pScreen == pWin->drawable.pScreen)
|
|
{
|
|
event->event_x = event->root_x - FP1616(pWin->drawable.x, 0);
|
|
event->event_y = event->root_y - FP1616(pWin->drawable.y, 0);
|
|
event->child = child;
|
|
} else
|
|
{
|
|
event->event_x = 0;
|
|
event->event_y = 0;
|
|
event->child = None;
|
|
}
|
|
|
|
if (event->evtype == XI_Enter || event->evtype == XI_Leave ||
|
|
event->evtype == XI_FocusIn || event->evtype == XI_FocusOut)
|
|
((xXIEnterEvent*)event)->same_screen =
|
|
(pSprite->hot.pScreen == pWin->drawable.pScreen);
|
|
|
|
} else
|
|
{
|
|
XE_KBPTR.root = RootWindow(pSprite)->drawable.id;
|
|
XE_KBPTR.event = pWin->drawable.id;
|
|
if (pSprite->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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a given event is deliverable at all on a given window.
|
|
*
|
|
* This function only checks if any client wants it, not for a specific
|
|
* client.
|
|
*
|
|
* @param[in] dev The device this event is being sent for.
|
|
* @param[in] evtype The event type of the event that is to be sent.
|
|
* @param[in] win The current event window.
|
|
*
|
|
* @return Bitmask of ::EVENT_XI2_MASK, ::EVENT_XI1_MASK, ::EVENT_CORE_MASK, and
|
|
* ::EVENT_DONT_PROPAGATE_MASK.
|
|
*/
|
|
int
|
|
EventIsDeliverable(DeviceIntPtr dev, int evtype, WindowPtr win)
|
|
{
|
|
int rc = 0;
|
|
int filter = 0;
|
|
int type;
|
|
OtherInputMasks *inputMasks = wOtherInputMasks(win);
|
|
|
|
if ((type = GetXI2Type(evtype)) != 0)
|
|
{
|
|
if (inputMasks && xi2mask_isset(inputMasks->xi2mask, dev, type))
|
|
rc |= EVENT_XI2_MASK;
|
|
}
|
|
|
|
if ((type = GetXIType(evtype)) != 0)
|
|
{
|
|
filter = event_get_filter_from_type(dev, type);
|
|
|
|
/* Check for XI mask */
|
|
if (inputMasks &&
|
|
(inputMasks->deliverableEvents[dev->id] & filter) &&
|
|
(inputMasks->inputEvents[dev->id] & filter))
|
|
rc |= EVENT_XI1_MASK;
|
|
|
|
/* Check for XI DontPropagate mask */
|
|
if (inputMasks && (inputMasks->dontPropagateMask[dev->id] & filter))
|
|
rc |= EVENT_DONT_PROPAGATE_MASK;
|
|
|
|
}
|
|
|
|
if ((type = GetCoreType(evtype)) != 0)
|
|
{
|
|
filter = event_get_filter_from_type(dev, type);
|
|
|
|
/* Check for core mask */
|
|
if ((win->deliverableEvents & filter) &&
|
|
((wOtherEventMasks(win) | win->eventMask) & filter))
|
|
rc |= EVENT_CORE_MASK;
|
|
|
|
/* Check for core DontPropagate mask */
|
|
if (filter & wDontPropagateMask(win))
|
|
rc |= EVENT_DONT_PROPAGATE_MASK;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
DeliverEvent(DeviceIntPtr dev, xEvent *xE, int count,
|
|
WindowPtr win, Window child, GrabPtr grab)
|
|
{
|
|
SpritePtr pSprite = dev->spriteInfo->sprite;
|
|
Mask filter;
|
|
int deliveries = 0;
|
|
|
|
if (XaceHook(XACE_SEND_ACCESS, NULL, dev, win, xE, count) == Success) {
|
|
filter = GetEventFilter(dev, xE);
|
|
FixUpEventFromWindow(pSprite, xE, win, child, FALSE);
|
|
deliveries = DeliverEventsToWindow(dev, win, xE, count,
|
|
filter, grab);
|
|
}
|
|
|
|
return deliveries;
|
|
}
|
|
|
|
static int
|
|
DeliverOneEvent(InternalEvent *event, DeviceIntPtr dev, enum InputLevel level,
|
|
WindowPtr win, Window child, GrabPtr grab)
|
|
{
|
|
xEvent *xE = NULL;
|
|
int count = 0;
|
|
int deliveries = 0;
|
|
int rc;
|
|
|
|
switch(level)
|
|
{
|
|
case XI2:
|
|
rc = EventToXI2(event, &xE);
|
|
count = 1;
|
|
break;
|
|
case XI:
|
|
rc = EventToXI(event, &xE, &count);
|
|
break;
|
|
case CORE:
|
|
rc = EventToCore(event, &xE, &count);
|
|
break;
|
|
}
|
|
|
|
if (rc == Success)
|
|
{
|
|
deliveries = DeliverEvent(dev, xE, count, win, child, grab);
|
|
free(xE);
|
|
} else
|
|
BUG_WARN_MSG(rc != BadMatch, "%s: conversion to level %d failed with rc %d\n",
|
|
dev->name, level, rc);
|
|
return deliveries;
|
|
}
|
|
|
|
/**
|
|
* Deliver events caused by input devices.
|
|
*
|
|
* For events from a non-grabbed, non-focus device, DeliverDeviceEvents is
|
|
* called directly from the processInputProc.
|
|
* For grabbed devices, DeliverGrabbedEvent is called first, and _may_ call
|
|
* DeliverDeviceEvents.
|
|
* For focused events, DeliverFocusedEvent is called first, and _may_ call
|
|
* DeliverDeviceEvents.
|
|
*
|
|
* @param pWin Window to deliver event to.
|
|
* @param event The events to deliver, not yet in wire format.
|
|
* @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.
|
|
*
|
|
* @see DeliverGrabbedEvent
|
|
* @see DeliverFocusedEvent
|
|
*/
|
|
int
|
|
DeliverDeviceEvents(WindowPtr pWin, InternalEvent *event, GrabPtr grab,
|
|
WindowPtr stopAt, DeviceIntPtr dev)
|
|
{
|
|
Window child = None;
|
|
int deliveries = 0;
|
|
int mask;
|
|
|
|
verify_internal_event(event);
|
|
|
|
while (pWin)
|
|
{
|
|
if ((mask = EventIsDeliverable(dev, event->any.type, pWin)))
|
|
{
|
|
/* XI2 events first */
|
|
if (mask & EVENT_XI2_MASK)
|
|
{
|
|
deliveries = DeliverOneEvent(event, dev, XI2, pWin, child, grab);
|
|
if (deliveries > 0)
|
|
break;
|
|
}
|
|
|
|
/* XI events */
|
|
if (mask & EVENT_XI1_MASK)
|
|
{
|
|
deliveries = DeliverOneEvent(event, dev, XI, pWin, child, grab);
|
|
if (deliveries > 0)
|
|
break;
|
|
}
|
|
|
|
/* Core event */
|
|
if ((mask & EVENT_CORE_MASK) && IsMaster(dev) && dev->coreEvents)
|
|
{
|
|
deliveries = DeliverOneEvent(event, dev, CORE, pWin, child, grab);
|
|
if (deliveries > 0)
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if ((deliveries < 0) || (pWin == stopAt) ||
|
|
(mask & EVENT_DONT_PROPAGATE_MASK))
|
|
{
|
|
deliveries = 0;
|
|
break;
|
|
}
|
|
|
|
child = pWin->drawable.id;
|
|
pWin = pWin->parent;
|
|
}
|
|
|
|
return deliveries;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
int
|
|
DeliverEvents(WindowPtr pWin, xEvent *xE, int count,
|
|
WindowPtr otherParent)
|
|
{
|
|
DeviceIntRec dummy;
|
|
int deliveries;
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension && pWin->drawable.pScreen->myNum)
|
|
return count;
|
|
#endif
|
|
|
|
if (!count)
|
|
return 0;
|
|
|
|
dummy.id = XIAllDevices;
|
|
|
|
switch (xE->u.u.type)
|
|
{
|
|
case DestroyNotify:
|
|
case UnmapNotify:
|
|
case MapNotify:
|
|
case MapRequest:
|
|
case ReparentNotify:
|
|
case ConfigureNotify:
|
|
case ConfigureRequest:
|
|
case GravityNotify:
|
|
case CirculateNotify:
|
|
case CirculateRequest:
|
|
xE->u.destroyNotify.event = pWin->drawable.id;
|
|
break;
|
|
}
|
|
|
|
switch (xE->u.u.type)
|
|
{
|
|
case DestroyNotify:
|
|
case UnmapNotify:
|
|
case MapNotify:
|
|
case ReparentNotify:
|
|
case ConfigureNotify:
|
|
case GravityNotify:
|
|
case CirculateNotify:
|
|
break;
|
|
default:
|
|
{
|
|
Mask filter;
|
|
filter = GetEventFilter(&dummy, xE);
|
|
return DeliverEventsToWindow(&dummy, pWin, xE, count, filter,
|
|
NullGrab);
|
|
}
|
|
}
|
|
|
|
deliveries = DeliverEventsToWindow(&dummy, pWin, xE, count,
|
|
StructureNotifyMask, NullGrab);
|
|
if (pWin->parent)
|
|
{
|
|
xE->u.destroyNotify.event = pWin->parent->drawable.id;
|
|
deliveries += DeliverEventsToWindow(&dummy, pWin->parent, xE, count,
|
|
SubstructureNotifyMask, NullGrab);
|
|
if (xE->u.u.type == ReparentNotify)
|
|
{
|
|
xE->u.destroyNotify.event = otherParent->drawable.id;
|
|
deliveries += DeliverEventsToWindow(&dummy,
|
|
otherParent, xE, count, SubstructureNotifyMask,
|
|
NullGrab);
|
|
}
|
|
}
|
|
return deliveries;
|
|
}
|
|
|
|
|
|
static Bool
|
|
PointInBorderSize(WindowPtr pWin, int x, int y)
|
|
{
|
|
BoxRec box;
|
|
|
|
if(RegionContainsPoint(&pWin->borderSize, x, y, &box))
|
|
return TRUE;
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension &&
|
|
XineramaSetWindowPntrs(inputInfo.pointer, pWin)) {
|
|
SpritePtr pSprite = inputInfo.pointer->spriteInfo->sprite;
|
|
int i;
|
|
|
|
FOR_NSCREENS_FORWARD_SKIP(i) {
|
|
if(RegionContainsPoint(&pSprite->windows[i]->borderSize,
|
|
x + screenInfo.screens[0]->x - screenInfo.screens[i]->x,
|
|
y + screenInfo.screens[0]->y - screenInfo.screens[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.
|
|
*/
|
|
WindowPtr
|
|
XYToWindow(SpritePtr pSprite, int x, int y)
|
|
{
|
|
WindowPtr pWin;
|
|
BoxRec box;
|
|
|
|
pSprite->spriteTraceGood = 1; /* root window still there */
|
|
pWin = RootWindow(pSprite)->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))
|
|
/* 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) ||
|
|
RegionContainsPoint(wInputShape(pWin),
|
|
x - pWin->drawable.x,
|
|
y - pWin->drawable.y, &box))
|
|
#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 (pSprite->spriteTraceGood >= pSprite->spriteTraceSize)
|
|
{
|
|
pSprite->spriteTraceSize += 10;
|
|
pSprite->spriteTrace = realloc(pSprite->spriteTrace,
|
|
pSprite->spriteTraceSize*sizeof(WindowPtr));
|
|
}
|
|
pSprite->spriteTrace[pSprite->spriteTraceGood++] = pWin;
|
|
pWin = pWin->firstChild;
|
|
}
|
|
else
|
|
pWin = pWin->nextSib;
|
|
}
|
|
return DeepestSpriteWin(pSprite);
|
|
}
|
|
|
|
/**
|
|
* Ungrab a currently FocusIn grabbed device and grab the device on the
|
|
* given window. If the win given is the NoneWin, the device is ungrabbed if
|
|
* applicable and FALSE is returned.
|
|
*
|
|
* @returns TRUE if the device has been grabbed, or FALSE otherwise.
|
|
*/
|
|
BOOL
|
|
ActivateFocusInGrab(DeviceIntPtr dev, WindowPtr old, WindowPtr win)
|
|
{
|
|
BOOL rc = FALSE;
|
|
DeviceEvent event;
|
|
|
|
if (dev->deviceGrab.grab)
|
|
{
|
|
if (!dev->deviceGrab.fromPassiveGrab ||
|
|
dev->deviceGrab.grab->type != XI_Enter ||
|
|
dev->deviceGrab.grab->window == win ||
|
|
IsParent(dev->deviceGrab.grab->window, win))
|
|
return FALSE;
|
|
DoEnterLeaveEvents(dev, dev->id, old, win, XINotifyPassiveUngrab);
|
|
(*dev->deviceGrab.DeactivateGrab)(dev);
|
|
}
|
|
|
|
if (win == NoneWin || win == PointerRootWin)
|
|
return FALSE;
|
|
|
|
memset(&event, 0, sizeof(DeviceEvent));
|
|
event.header = ET_Internal;
|
|
event.type = ET_FocusIn;
|
|
event.length = sizeof(DeviceEvent);
|
|
event.time = GetTimeInMillis();
|
|
event.deviceid = dev->id;
|
|
event.sourceid = dev->id;
|
|
event.detail.button = 0;
|
|
rc = (CheckPassiveGrabsOnWindow(win, dev, (InternalEvent *) &event, FALSE,
|
|
TRUE) != NULL);
|
|
if (rc)
|
|
DoEnterLeaveEvents(dev, dev->id, old, win, XINotifyPassiveUngrab);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Ungrab a currently Enter grabbed device and grab the device for the given
|
|
* window.
|
|
*
|
|
* @returns TRUE if the device has been grabbed, or FALSE otherwise.
|
|
*/
|
|
static BOOL
|
|
ActivateEnterGrab(DeviceIntPtr dev, WindowPtr old, WindowPtr win)
|
|
{
|
|
BOOL rc = FALSE;
|
|
DeviceEvent event;
|
|
|
|
if (dev->deviceGrab.grab)
|
|
{
|
|
if (!dev->deviceGrab.fromPassiveGrab ||
|
|
dev->deviceGrab.grab->type != XI_Enter ||
|
|
dev->deviceGrab.grab->window == win ||
|
|
IsParent(dev->deviceGrab.grab->window, win))
|
|
return FALSE;
|
|
DoEnterLeaveEvents(dev, dev->id, old, win, XINotifyPassiveUngrab);
|
|
(*dev->deviceGrab.DeactivateGrab)(dev);
|
|
}
|
|
|
|
memset(&event, 0, sizeof(DeviceEvent));
|
|
event.header = ET_Internal;
|
|
event.type = ET_Enter;
|
|
event.length = sizeof(DeviceEvent);
|
|
event.time = GetTimeInMillis();
|
|
event.deviceid = dev->id;
|
|
event.sourceid = dev->id;
|
|
event.detail.button = 0;
|
|
rc = (CheckPassiveGrabsOnWindow(win, dev, (InternalEvent *) &event, FALSE,
|
|
TRUE) != NULL);
|
|
if (rc)
|
|
DoEnterLeaveEvents(dev, dev->id, old, win, XINotifyPassiveGrab);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* CheckMotion() will not do anything and return FALSE if the event is not a
|
|
* pointer event.
|
|
*
|
|
* @return TRUE if the sprite has moved or FALSE otherwise.
|
|
*/
|
|
Bool
|
|
CheckMotion(DeviceEvent *ev, DeviceIntPtr pDev)
|
|
{
|
|
WindowPtr prevSpriteWin, newSpriteWin;
|
|
SpritePtr pSprite = pDev->spriteInfo->sprite;
|
|
|
|
verify_internal_event((InternalEvent *)ev);
|
|
|
|
prevSpriteWin = pSprite->win;
|
|
|
|
if (ev && !syncEvents.playingEvents)
|
|
{
|
|
/* GetPointerEvents() guarantees that pointer events have the correct
|
|
rootX/Y set already. */
|
|
switch (ev->type)
|
|
{
|
|
case ET_ButtonPress:
|
|
case ET_ButtonRelease:
|
|
case ET_Motion:
|
|
break;
|
|
default:
|
|
/* all other events return FALSE */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
{
|
|
/* Motion events entering DIX get translated to Screen 0
|
|
coordinates. Replayed events have already been
|
|
translated since they've entered DIX before */
|
|
ev->root_x += pSprite->screen->x - screenInfo.screens[0]->x;
|
|
ev->root_y += pSprite->screen->y - screenInfo.screens[0]->y;
|
|
} else
|
|
#endif
|
|
{
|
|
if (pSprite->hot.pScreen != pSprite->hotPhys.pScreen)
|
|
{
|
|
pSprite->hot.pScreen = pSprite->hotPhys.pScreen;
|
|
RootWindow(pDev->spriteInfo->sprite) =
|
|
pSprite->hot.pScreen->root;
|
|
}
|
|
}
|
|
|
|
pSprite->hot.x = ev->root_x;
|
|
pSprite->hot.y = ev->root_y;
|
|
if (pSprite->hot.x < pSprite->physLimits.x1)
|
|
pSprite->hot.x = pSprite->physLimits.x1;
|
|
else if (pSprite->hot.x >= pSprite->physLimits.x2)
|
|
pSprite->hot.x = pSprite->physLimits.x2 - 1;
|
|
if (pSprite->hot.y < pSprite->physLimits.y1)
|
|
pSprite->hot.y = pSprite->physLimits.y1;
|
|
else if (pSprite->hot.y >= pSprite->physLimits.y2)
|
|
pSprite->hot.y = pSprite->physLimits.y2 - 1;
|
|
if (pSprite->hotShape)
|
|
ConfineToShape(pDev, pSprite->hotShape, &pSprite->hot.x, &pSprite->hot.y);
|
|
pSprite->hotPhys = pSprite->hot;
|
|
|
|
if ((pSprite->hotPhys.x != ev->root_x) ||
|
|
(pSprite->hotPhys.y != ev->root_y))
|
|
{
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
{
|
|
XineramaSetCursorPosition(
|
|
pDev, pSprite->hotPhys.x, pSprite->hotPhys.y, FALSE);
|
|
} else
|
|
#endif
|
|
{
|
|
(*pSprite->hotPhys.pScreen->SetCursorPosition)(
|
|
pDev, pSprite->hotPhys.pScreen,
|
|
pSprite->hotPhys.x, pSprite->hotPhys.y, FALSE);
|
|
}
|
|
}
|
|
|
|
ev->root_x = pSprite->hot.x;
|
|
ev->root_y = pSprite->hot.y;
|
|
}
|
|
|
|
newSpriteWin = XYToWindow(pSprite, pSprite->hot.x, pSprite->hot.y);
|
|
|
|
if (newSpriteWin != prevSpriteWin)
|
|
{
|
|
int sourceid;
|
|
if (!ev) {
|
|
UpdateCurrentTimeIf();
|
|
sourceid = pDev->id; /* when from WindowsRestructured */
|
|
} else
|
|
sourceid = ev->sourceid;
|
|
|
|
if (prevSpriteWin != NullWindow) {
|
|
if (!ActivateEnterGrab(pDev, prevSpriteWin, newSpriteWin))
|
|
DoEnterLeaveEvents(pDev, sourceid, prevSpriteWin,
|
|
newSpriteWin, NotifyNormal);
|
|
}
|
|
/* set pSprite->win after ActivateEnterGrab, otherwise
|
|
sprite window == grab_window and no enter/leave events are
|
|
sent. */
|
|
pSprite->win = newSpriteWin;
|
|
PostNewCursor(pDev);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Windows have restructured, we need to update the sprite position and the
|
|
* sprite's cursor.
|
|
*/
|
|
void
|
|
WindowsRestructured(void)
|
|
{
|
|
DeviceIntPtr pDev = inputInfo.devices;
|
|
while(pDev)
|
|
{
|
|
if (IsMaster(pDev) || IsFloating(pDev))
|
|
CheckMotion(NULL, pDev);
|
|
pDev = pDev->next;
|
|
}
|
|
}
|
|
|
|
#ifdef PANORAMIX
|
|
/* This was added to support reconfiguration under Xdmx. The problem is
|
|
* that if the 0th screen (i.e., screenInfo.screens[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;
|
|
DeviceIntPtr pDev;
|
|
SpritePtr pSprite;
|
|
|
|
if (noPanoramiXExtension) return;
|
|
|
|
pDev = inputInfo.devices;
|
|
while(pDev)
|
|
{
|
|
if (DevHasCursor(pDev))
|
|
{
|
|
pSprite = pDev->spriteInfo->sprite;
|
|
pSprite->hot.x -= xoff;
|
|
pSprite->hot.y -= yoff;
|
|
|
|
pSprite->hotPhys.x -= xoff;
|
|
pSprite->hotPhys.y -= yoff;
|
|
|
|
pSprite->hotLimits.x1 -= xoff;
|
|
pSprite->hotLimits.y1 -= yoff;
|
|
pSprite->hotLimits.x2 -= xoff;
|
|
pSprite->hotLimits.y2 -= yoff;
|
|
|
|
if (RegionNotEmpty(&pSprite->Reg1))
|
|
RegionTranslate(&pSprite->Reg1, xoff, yoff);
|
|
if (RegionNotEmpty(&pSprite->Reg2))
|
|
RegionTranslate(&pSprite->Reg2, xoff, yoff);
|
|
|
|
/* FIXME: if we call ConfineCursorToWindow, must we do anything else? */
|
|
if ((grab = pDev->deviceGrab.grab) && grab->confineTo) {
|
|
if (grab->confineTo->drawable.pScreen
|
|
!= pSprite->hotPhys.pScreen)
|
|
pSprite->hotPhys.x = pSprite->hotPhys.y = 0;
|
|
ConfineCursorToWindow(pDev, grab->confineTo, TRUE, TRUE);
|
|
} else
|
|
ConfineCursorToWindow(
|
|
pDev,
|
|
pSprite->hotPhys.pScreen->root,
|
|
TRUE, FALSE);
|
|
|
|
}
|
|
pDev = pDev->next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Initialize a sprite for the given device and set it to some sane values. If
|
|
* the device already has a sprite alloc'd, don't realloc but just reset to
|
|
* default values.
|
|
* If a window is supplied, the sprite will be initialized with the window's
|
|
* cursor and positioned in the center of the window's screen. The root window
|
|
* is a good choice to pass in here.
|
|
*
|
|
* It's a good idea to call it only for pointer devices, unless you have a
|
|
* really talented keyboard.
|
|
*
|
|
* @param pDev The device to initialize.
|
|
* @param pWin The window where to generate the sprite in.
|
|
*
|
|
*/
|
|
void
|
|
InitializeSprite(DeviceIntPtr pDev, WindowPtr pWin)
|
|
{
|
|
SpritePtr pSprite;
|
|
ScreenPtr pScreen;
|
|
CursorPtr pCursor;
|
|
|
|
if (!pDev->spriteInfo->sprite)
|
|
{
|
|
DeviceIntPtr it;
|
|
|
|
pDev->spriteInfo->sprite = (SpritePtr)calloc(1, sizeof(SpriteRec));
|
|
if (!pDev->spriteInfo->sprite)
|
|
FatalError("InitializeSprite: failed to allocate sprite struct");
|
|
|
|
/* We may have paired another device with this device before our
|
|
* device had a actual sprite. We need to check for this and reset the
|
|
* sprite field for all paired devices.
|
|
*
|
|
* The VCK is always paired with the VCP before the VCP has a sprite.
|
|
*/
|
|
for (it = inputInfo.devices; it; it = it->next)
|
|
{
|
|
if (it->spriteInfo->paired == pDev)
|
|
it->spriteInfo->sprite = pDev->spriteInfo->sprite;
|
|
}
|
|
if (inputInfo.keyboard->spriteInfo->paired == pDev)
|
|
inputInfo.keyboard->spriteInfo->sprite = pDev->spriteInfo->sprite;
|
|
}
|
|
|
|
pSprite = pDev->spriteInfo->sprite;
|
|
pDev->spriteInfo->spriteOwner = TRUE;
|
|
|
|
pScreen = (pWin) ? pWin->drawable.pScreen : (ScreenPtr)NULL;
|
|
pSprite->hot.pScreen = pScreen;
|
|
pSprite->hotPhys.pScreen = pScreen;
|
|
if (pScreen)
|
|
{
|
|
pSprite->hotPhys.x = pScreen->width / 2;
|
|
pSprite->hotPhys.y = pScreen->height / 2;
|
|
pSprite->hotLimits.x2 = pScreen->width;
|
|
pSprite->hotLimits.y2 = pScreen->height;
|
|
}
|
|
|
|
pSprite->hot = pSprite->hotPhys;
|
|
pSprite->win = pWin;
|
|
|
|
if (pWin)
|
|
{
|
|
pCursor = wCursor(pWin);
|
|
pSprite->spriteTrace = (WindowPtr *)calloc(1, 32*sizeof(WindowPtr));
|
|
if (!pSprite->spriteTrace)
|
|
FatalError("Failed to allocate spriteTrace");
|
|
pSprite->spriteTraceSize = 32;
|
|
|
|
RootWindow(pDev->spriteInfo->sprite) = pWin;
|
|
pSprite->spriteTraceGood = 1;
|
|
|
|
pSprite->pEnqueueScreen = pScreen;
|
|
pSprite->pDequeueScreen = pSprite->pEnqueueScreen;
|
|
|
|
} else {
|
|
pCursor = NullCursor;
|
|
pSprite->spriteTrace = NULL;
|
|
pSprite->spriteTraceSize = 0;
|
|
pSprite->spriteTraceGood = 0;
|
|
pSprite->pEnqueueScreen = screenInfo.screens[0];
|
|
pSprite->pDequeueScreen = pSprite->pEnqueueScreen;
|
|
}
|
|
if (pCursor)
|
|
pCursor->refcnt++;
|
|
if (pSprite->current)
|
|
FreeCursor(pSprite->current, None);
|
|
pSprite->current = pCursor;
|
|
|
|
if (pScreen)
|
|
{
|
|
(*pScreen->RealizeCursor) ( pDev, pScreen, pSprite->current);
|
|
(*pScreen->CursorLimits) ( pDev, pScreen, pSprite->current,
|
|
&pSprite->hotLimits, &pSprite->physLimits);
|
|
pSprite->confined = FALSE;
|
|
|
|
(*pScreen->ConstrainCursor) (pDev, pScreen,
|
|
&pSprite->physLimits);
|
|
(*pScreen->SetCursorPosition) (pDev, pScreen, pSprite->hot.x,
|
|
pSprite->hot.y,
|
|
FALSE);
|
|
(*pScreen->DisplayCursor) (pDev, pScreen, pSprite->current);
|
|
}
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension) {
|
|
pSprite->hotLimits.x1 = -screenInfo.screens[0]->x;
|
|
pSprite->hotLimits.y1 = -screenInfo.screens[0]->y;
|
|
pSprite->hotLimits.x2 = PanoramiXPixWidth - screenInfo.screens[0]->x;
|
|
pSprite->hotLimits.y2 = PanoramiXPixHeight - screenInfo.screens[0]->y;
|
|
pSprite->physLimits = pSprite->hotLimits;
|
|
pSprite->confineWin = NullWindow;
|
|
pSprite->hotShape = NullRegion;
|
|
pSprite->screen = pScreen;
|
|
/* gotta UNINIT these someplace */
|
|
RegionNull(&pSprite->Reg1);
|
|
RegionNull(&pSprite->Reg2);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Update the mouse sprite info when the server switches from a pScreen to another.
|
|
* Otherwise, the pScreen of the mouse sprite is never updated when we switch
|
|
* from a pScreen to another. Never updating the pScreen of the mouse sprite
|
|
* implies that windows that are in pScreen whose pScreen->myNum >0 will never
|
|
* get pointer events. This is because in CheckMotion(), sprite.hotPhys.pScreen
|
|
* always points to the first pScreen it has been set by
|
|
* DefineInitialRootWindow().
|
|
*
|
|
* Calling this function is useful for use cases where the server
|
|
* has more than one pScreen.
|
|
* This function is similar to DefineInitialRootWindow() but it does not
|
|
* reset the mouse pointer position.
|
|
* @param win must be the new pScreen we are switching to.
|
|
*/
|
|
void
|
|
UpdateSpriteForScreen(DeviceIntPtr pDev, ScreenPtr pScreen)
|
|
{
|
|
SpritePtr pSprite = NULL;
|
|
WindowPtr win = NULL;
|
|
CursorPtr pCursor;
|
|
if (!pScreen)
|
|
return ;
|
|
|
|
if (!pDev->spriteInfo->sprite)
|
|
return;
|
|
|
|
pSprite = pDev->spriteInfo->sprite;
|
|
|
|
win = pScreen->root;
|
|
|
|
pSprite->hotPhys.pScreen = pScreen;
|
|
pSprite->hot = pSprite->hotPhys;
|
|
pSprite->hotLimits.x2 = pScreen->width;
|
|
pSprite->hotLimits.y2 = pScreen->height;
|
|
pSprite->win = win;
|
|
pCursor = wCursor(win);
|
|
if (pCursor)
|
|
pCursor->refcnt++;
|
|
if (pSprite->current)
|
|
FreeCursor(pSprite->current, 0);
|
|
pSprite->current = pCursor;
|
|
pSprite->spriteTraceGood = 1;
|
|
pSprite->spriteTrace[0] = win;
|
|
(*pScreen->CursorLimits) (pDev,
|
|
pScreen,
|
|
pSprite->current,
|
|
&pSprite->hotLimits,
|
|
&pSprite->physLimits);
|
|
pSprite->confined = FALSE;
|
|
(*pScreen->ConstrainCursor) (pDev, pScreen, &pSprite->physLimits);
|
|
(*pScreen->DisplayCursor) (pDev, pScreen, pSprite->current);
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension) {
|
|
pSprite->hotLimits.x1 = -screenInfo.screens[0]->x;
|
|
pSprite->hotLimits.y1 = -screenInfo.screens[0]->y;
|
|
pSprite->hotLimits.x2 = PanoramiXPixWidth - screenInfo.screens[0]->x;
|
|
pSprite->hotLimits.y2 = PanoramiXPixHeight - screenInfo.screens[0]->y;
|
|
pSprite->physLimits = pSprite->hotLimits;
|
|
pSprite->screen = pScreen;
|
|
}
|
|
#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)
|
|
{
|
|
DeviceIntPtr pDev;
|
|
|
|
for(pDev = inputInfo.devices; pDev; pDev = pDev->next)
|
|
if (DevHasCursor(pDev))
|
|
PostNewCursor(pDev);
|
|
}
|
|
|
|
void
|
|
NewCurrentScreen(DeviceIntPtr pDev, ScreenPtr newScreen, int x, int y)
|
|
{
|
|
DeviceIntPtr ptr;
|
|
SpritePtr pSprite;
|
|
|
|
ptr = IsFloating(pDev) ? pDev : GetXTestDevice(GetMaster(pDev, MASTER_POINTER));
|
|
pSprite = ptr->spriteInfo->sprite;
|
|
|
|
pSprite->hotPhys.x = x;
|
|
pSprite->hotPhys.y = y;
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension) {
|
|
pSprite->hotPhys.x += newScreen->x - screenInfo.screens[0]->x;
|
|
pSprite->hotPhys.y += newScreen->y - screenInfo.screens[0]->y;
|
|
if (newScreen != pSprite->screen) {
|
|
pSprite->screen = newScreen;
|
|
/* Make sure we tell the DDX to update its copy of the screen */
|
|
if(pSprite->confineWin)
|
|
XineramaConfineCursorToWindow(ptr,
|
|
pSprite->confineWin, TRUE);
|
|
else
|
|
XineramaConfineCursorToWindow(ptr, screenInfo.screens[0]->root, 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)
|
|
(*pSprite->screen->SetCursorPosition)(
|
|
ptr,
|
|
pSprite->screen,
|
|
pSprite->hotPhys.x + screenInfo.screens[0]->x -
|
|
pSprite->screen->x,
|
|
pSprite->hotPhys.y + screenInfo.screens[0]->y -
|
|
pSprite->screen->y, FALSE);
|
|
}
|
|
} else
|
|
#endif
|
|
if (newScreen != pSprite->hotPhys.pScreen)
|
|
ConfineCursorToWindow(ptr, newScreen->root, TRUE, FALSE);
|
|
}
|
|
|
|
#ifdef PANORAMIX
|
|
|
|
static Bool
|
|
XineramaPointInWindowIsVisible(
|
|
WindowPtr pWin,
|
|
int x,
|
|
int y
|
|
)
|
|
{
|
|
BoxRec box;
|
|
int i, xoff, yoff;
|
|
|
|
if (!pWin->realized) return FALSE;
|
|
|
|
if (RegionContainsPoint(&pWin->borderClip, x, y, &box))
|
|
return TRUE;
|
|
|
|
if(!XineramaSetWindowPntrs(inputInfo.pointer, pWin)) return FALSE;
|
|
|
|
xoff = x + screenInfo.screens[0]->x;
|
|
yoff = y + screenInfo.screens[0]->y;
|
|
|
|
FOR_NSCREENS_FORWARD_SKIP(i) {
|
|
pWin = inputInfo.pointer->spriteInfo->sprite->windows[i];
|
|
x = xoff - screenInfo.screens[i]->x;
|
|
y = yoff - screenInfo.screens[i]->y;
|
|
|
|
if(RegionContainsPoint(&pWin->borderClip, x, y, &box)
|
|
&& (!wInputShape(pWin) ||
|
|
RegionContainsPoint(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;
|
|
DeviceIntPtr dev;
|
|
SpritePtr pSprite;
|
|
|
|
REQUEST(xWarpPointerReq);
|
|
|
|
if (stuff->dstWid != None) {
|
|
rc = dixLookupWindow(&dest, stuff->dstWid, client, DixReadAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
}
|
|
|
|
/* Post through the XTest device */
|
|
dev = PickPointer(client);
|
|
dev = GetXTestDevice(dev);
|
|
pSprite = dev->spriteInfo->sprite;
|
|
x = pSprite->hotPhys.x;
|
|
y = pSprite->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 == screenInfo.screens[0]->root) {
|
|
winX -= screenInfo.screens[0]->x;
|
|
winY -= screenInfo.screens[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 == screenInfo.screens[0]->root) {
|
|
x -= screenInfo.screens[0]->x;
|
|
y -= screenInfo.screens[0]->y;
|
|
}
|
|
}
|
|
|
|
x += stuff->dstX;
|
|
y += stuff->dstY;
|
|
|
|
if (x < pSprite->physLimits.x1)
|
|
x = pSprite->physLimits.x1;
|
|
else if (x >= pSprite->physLimits.x2)
|
|
x = pSprite->physLimits.x2 - 1;
|
|
if (y < pSprite->physLimits.y1)
|
|
y = pSprite->physLimits.y1;
|
|
else if (y >= pSprite->physLimits.y2)
|
|
y = pSprite->physLimits.y2 - 1;
|
|
if (pSprite->hotShape)
|
|
ConfineToShape(dev, pSprite->hotShape, &x, &y);
|
|
|
|
XineramaSetCursorPosition(dev, 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;
|
|
DeviceIntPtr dev, tmp, xtest_dev = NULL;
|
|
SpritePtr pSprite;
|
|
|
|
REQUEST(xWarpPointerReq);
|
|
REQUEST_SIZE_MATCH(xWarpPointerReq);
|
|
|
|
dev = PickPointer(client);
|
|
|
|
for (tmp = inputInfo.devices; tmp; tmp = tmp->next) {
|
|
if (GetMaster(tmp, MASTER_ATTACHED) == dev) {
|
|
rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixWriteAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
if (IsXTestDevice(tmp, dev))
|
|
xtest_dev = tmp;
|
|
}
|
|
}
|
|
|
|
/* Use the XTest device to actually move the pointer */
|
|
dev = xtest_dev;
|
|
pSprite = dev->spriteInfo->sprite;
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension)
|
|
return XineramaWarpPointer(client);
|
|
#endif
|
|
|
|
if (stuff->dstWid != None) {
|
|
rc = dixLookupWindow(&dest, stuff->dstWid, client, DixGetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
}
|
|
x = pSprite->hotPhys.x;
|
|
y = pSprite->hotPhys.y;
|
|
|
|
if (stuff->srcWid != None)
|
|
{
|
|
int winX, winY;
|
|
XID winID = stuff->srcWid;
|
|
WindowPtr source;
|
|
|
|
rc = dixLookupWindow(&source, winID, client, DixGetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
winX = source->drawable.x;
|
|
winY = source->drawable.y;
|
|
if (source->drawable.pScreen != pSprite->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 = pSprite->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 == pSprite->hotPhys.pScreen)
|
|
{
|
|
if (x < pSprite->physLimits.x1)
|
|
x = pSprite->physLimits.x1;
|
|
else if (x >= pSprite->physLimits.x2)
|
|
x = pSprite->physLimits.x2 - 1;
|
|
if (y < pSprite->physLimits.y1)
|
|
y = pSprite->physLimits.y1;
|
|
else if (y >= pSprite->physLimits.y2)
|
|
y = pSprite->physLimits.y2 - 1;
|
|
if (pSprite->hotShape)
|
|
ConfineToShape(dev, pSprite->hotShape, &x, &y);
|
|
(*newScreen->SetCursorPosition)(dev, newScreen, x, y, TRUE);
|
|
}
|
|
else if (!PointerConfinedToScreen(dev))
|
|
{
|
|
NewCurrentScreen(dev, newScreen, x, y);
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
static Bool
|
|
BorderSizeNotEmpty(DeviceIntPtr pDev, WindowPtr pWin)
|
|
{
|
|
if(RegionNotEmpty(&pWin->borderSize))
|
|
return TRUE;
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension && XineramaSetWindowPntrs(pDev, pWin)) {
|
|
int i;
|
|
|
|
FOR_NSCREENS_FORWARD_SKIP(i) {
|
|
if(RegionNotEmpty(&pDev->spriteInfo->sprite->windows[i]->borderSize))
|
|
return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Activate the given passive grab. If the grab is activated successfully, the
|
|
* event has been delivered to the client.
|
|
*
|
|
* @param device The device of the event to check.
|
|
* @param grab The grab to check.
|
|
* @param event The current device event.
|
|
*
|
|
* @return Whether the grab has been activated.
|
|
*/
|
|
Bool
|
|
ActivatePassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event)
|
|
{
|
|
SpritePtr pSprite = device->spriteInfo->sprite;
|
|
GrabInfoPtr grabinfo = &device->deviceGrab;
|
|
xEvent *xE = NULL;
|
|
int count;
|
|
int rc;
|
|
|
|
/* The only consumers of corestate are Xi 1.x and core events, which
|
|
* are guaranteed to come from DeviceEvents. */
|
|
if (grab->grabtype == XI || grab->grabtype == CORE)
|
|
{
|
|
DeviceIntPtr gdev;
|
|
|
|
event->device_event.corestate &= 0x1f00;
|
|
|
|
if (grab->grabtype == CORE)
|
|
gdev = GetMaster(device, KEYBOARD_OR_FLOAT);
|
|
else
|
|
gdev = grab->modifierDevice;
|
|
|
|
if (gdev && gdev->key && gdev->key->xkbInfo)
|
|
event->device_event.corestate |=
|
|
gdev->key->xkbInfo->state.grab_mods & (~0x1f00);
|
|
}
|
|
|
|
if (grab->grabtype == CORE)
|
|
{
|
|
rc = EventToCore(event, &xE, &count);
|
|
if (rc != Success)
|
|
{
|
|
BUG_WARN_MSG(rc != BadMatch,"[dix] %s: core conversion failed"
|
|
"(%d, %d).\n", device->name, event->any.type, rc);
|
|
return FALSE;
|
|
}
|
|
} else if (grab->grabtype == XI2)
|
|
{
|
|
rc = EventToXI2(event, &xE);
|
|
if (rc != Success)
|
|
{
|
|
if (rc != BadMatch)
|
|
BUG_WARN_MSG(rc != BadMatch,"[dix] %s: XI2 conversion failed"
|
|
"(%d, %d).\n", device->name, event->any.type, rc);
|
|
return FALSE;
|
|
}
|
|
count = 1;
|
|
} else
|
|
{
|
|
rc = EventToXI(event, &xE, &count);
|
|
if (rc != Success)
|
|
{
|
|
if (rc != BadMatch)
|
|
BUG_WARN_MSG(rc != BadMatch,"[dix] %s: XI conversion failed"
|
|
"(%d, %d).\n", device->name, event->any.type, rc);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
(*grabinfo->ActivateGrab)(device, grab, currentTime, TRUE);
|
|
|
|
if (xE)
|
|
{
|
|
FixUpEventFromWindow(pSprite, xE, grab->window, None, TRUE);
|
|
|
|
/* XXX: XACE? */
|
|
TryClientEvents(rClient(grab), device, xE, count,
|
|
GetEventFilter(device, xE),
|
|
GetEventFilter(device, xE), grab);
|
|
}
|
|
|
|
if (grabinfo->sync.state == FROZEN_NO_EVENT)
|
|
grabinfo->sync.state = FROZEN_WITH_EVENT;
|
|
*grabinfo->sync.event = event->device_event;
|
|
|
|
free(xE);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
CoreGrabInterferes(DeviceIntPtr device, GrabPtr grab)
|
|
{
|
|
DeviceIntPtr other;
|
|
BOOL interfering = FALSE;
|
|
|
|
for (other = inputInfo.devices; other; other = other->next)
|
|
{
|
|
GrabPtr othergrab = other->deviceGrab.grab;
|
|
if (othergrab && othergrab->grabtype == CORE &&
|
|
SameClient(grab, rClient(othergrab)) &&
|
|
((IsPointerDevice(grab->device) &&
|
|
IsPointerDevice(othergrab->device)) ||
|
|
(IsKeyboardDevice(grab->device) &&
|
|
IsKeyboardDevice(othergrab->device))))
|
|
{
|
|
interfering = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return interfering;
|
|
}
|
|
|
|
enum MatchFlags {
|
|
NO_MATCH = 0x0,
|
|
CORE_MATCH = 0x1,
|
|
XI_MATCH = 0x2,
|
|
XI2_MATCH = 0x4,
|
|
};
|
|
|
|
/**
|
|
* Match the grab against the temporary grab on the given input level.
|
|
* Modifies the temporary grab pointer.
|
|
*
|
|
* @param grab The grab to match against
|
|
* @param tmp The temporary grab to use for matching
|
|
* @param level The input level we want to match on
|
|
* @param event_type Wire protocol event type
|
|
*
|
|
* @return The respective matched flag or 0 for no match
|
|
*/
|
|
static enum MatchFlags
|
|
MatchForType(const GrabPtr grab, GrabPtr tmp, enum InputLevel level, int event_type)
|
|
{
|
|
enum MatchFlags match;
|
|
BOOL ignore_device = FALSE;
|
|
int grabtype;
|
|
int evtype;
|
|
|
|
switch(level)
|
|
{
|
|
case XI2:
|
|
grabtype = XI2;
|
|
evtype = GetXI2Type(event_type);
|
|
BUG_WARN(!evtype);
|
|
match = XI2_MATCH;
|
|
break;
|
|
case XI:
|
|
grabtype = XI;
|
|
evtype = GetXIType(event_type);
|
|
match = XI_MATCH;
|
|
break;
|
|
case CORE:
|
|
grabtype = CORE;
|
|
evtype = GetCoreType(event_type);
|
|
match = CORE_MATCH;
|
|
ignore_device = TRUE;
|
|
break;
|
|
}
|
|
|
|
tmp->grabtype = grabtype;
|
|
tmp->type = evtype;
|
|
|
|
if (tmp->type && GrabMatchesSecond(tmp, grab, ignore_device))
|
|
return match;
|
|
|
|
return NO_MATCH;
|
|
}
|
|
|
|
/**
|
|
* Check an individual grab against an event to determine if a passive grab
|
|
* should be activated.
|
|
*
|
|
* @param device The device of the event to check.
|
|
* @param grab The grab to check.
|
|
* @param event The current device event.
|
|
* @param checkCore Check for core grabs too.
|
|
* @param tempGrab A pre-allocated temporary grab record for matching. This
|
|
* must have the window and device values filled in.
|
|
*
|
|
* @return Whether the grab matches the event.
|
|
*/
|
|
static Bool
|
|
CheckPassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event,
|
|
Bool checkCore, GrabPtr tempGrab)
|
|
{
|
|
DeviceIntPtr gdev;
|
|
XkbSrvInfoPtr xkbi = NULL;
|
|
enum MatchFlags match = 0;
|
|
|
|
gdev = grab->modifierDevice;
|
|
if (grab->grabtype == CORE)
|
|
{
|
|
gdev = GetMaster(device, KEYBOARD_OR_FLOAT);
|
|
} else if (grab->grabtype == XI2)
|
|
{
|
|
/* if the device is an attached slave device, gdev must be the
|
|
* attached master keyboard. Since the slave may have been
|
|
* reattached after the grab, the modifier device may not be the
|
|
* same. */
|
|
if (!IsMaster(grab->device) && !IsFloating(device))
|
|
gdev = GetMaster(device, MASTER_KEYBOARD);
|
|
}
|
|
|
|
if (gdev && gdev->key)
|
|
xkbi= gdev->key->xkbInfo;
|
|
tempGrab->modifierDevice = grab->modifierDevice;
|
|
tempGrab->modifiersDetail.exact = xkbi ? xkbi->state.grab_mods : 0;
|
|
|
|
/* Check for XI2 and XI grabs first */
|
|
match = MatchForType(grab, tempGrab, XI2, GetXI2Type(event->any.type));
|
|
|
|
if (!match)
|
|
match = MatchForType(grab, tempGrab, XI, GetXIType(event->any.type));
|
|
|
|
if (!match && checkCore)
|
|
match = MatchForType(grab, tempGrab, CORE, GetCoreType(event->any.type));
|
|
|
|
if (!match || (grab->confineTo &&
|
|
(!grab->confineTo->realized ||
|
|
!BorderSizeNotEmpty(device, grab->confineTo))))
|
|
return FALSE;
|
|
|
|
/* In some cases a passive core grab may exist, but the client
|
|
* already has a core grab on some other device. In this case we
|
|
* must not get the grab, otherwise we may never ungrab the
|
|
* device.
|
|
*/
|
|
|
|
if (grab->grabtype == CORE)
|
|
{
|
|
/* A passive grab may have been created for a different device
|
|
than it is assigned to at this point in time.
|
|
Update the grab's device and modifier device to reflect the
|
|
current state.
|
|
Since XGrabDeviceButton requires to specify the
|
|
modifierDevice explicitly, we don't override this choice.
|
|
*/
|
|
if (grab->type < GenericEvent)
|
|
{
|
|
grab->device = device;
|
|
grab->modifierDevice = GetMaster(device, MASTER_KEYBOARD);
|
|
}
|
|
|
|
if (CoreGrabInterferes(device, grab))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* "CheckPassiveGrabsOnWindow" checks to see if the event passed in causes a
|
|
* passive grab set on the window to be activated.
|
|
* If activate is true and a passive grab is found, it will be activated,
|
|
* and 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 event The current device event.
|
|
* @param checkCore Check for core grabs too.
|
|
* @param activate If a grab is found, activate it and deliver the event.
|
|
*/
|
|
|
|
GrabPtr
|
|
CheckPassiveGrabsOnWindow(
|
|
WindowPtr pWin,
|
|
DeviceIntPtr device,
|
|
InternalEvent *event,
|
|
BOOL checkCore,
|
|
BOOL activate)
|
|
{
|
|
GrabPtr grab = wPassiveGrabs(pWin);
|
|
GrabPtr tempGrab;
|
|
|
|
if (!grab)
|
|
return NULL;
|
|
|
|
tempGrab = AllocGrab();
|
|
|
|
/* Fill out the grab details, but leave the type for later before
|
|
* comparing */
|
|
switch (event->any.type)
|
|
{
|
|
case ET_KeyPress:
|
|
case ET_KeyRelease:
|
|
tempGrab->detail.exact = event->device_event.detail.key;
|
|
break;
|
|
case ET_ButtonPress:
|
|
case ET_ButtonRelease:
|
|
tempGrab->detail.exact = event->device_event.detail.button;
|
|
break;
|
|
default:
|
|
tempGrab->detail.exact = 0;
|
|
break;
|
|
}
|
|
tempGrab->window = pWin;
|
|
tempGrab->device = device;
|
|
tempGrab->detail.pMask = NULL;
|
|
tempGrab->modifiersDetail.pMask = NULL;
|
|
tempGrab->next = NULL;
|
|
|
|
for (; grab; grab = grab->next)
|
|
{
|
|
if (!CheckPassiveGrab(device, grab, event, checkCore, tempGrab))
|
|
continue;
|
|
|
|
if (activate && !ActivatePassiveGrab(device, grab, event))
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
FreeGrab(tempGrab);
|
|
return grab;
|
|
}
|
|
|
|
/**
|
|
* 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!
|
|
*
|
|
* The event we pass in must always be an XI event. From this, we then emulate
|
|
* the core event and then check for grabs.
|
|
*
|
|
* @param device The device that caused the event.
|
|
* @param xE The event to handle (Device{Button|Key}Press).
|
|
* @param count Number of events in list.
|
|
* @return TRUE if a grab has been activated or false otherwise.
|
|
*/
|
|
|
|
Bool
|
|
CheckDeviceGrabs(DeviceIntPtr device, DeviceEvent *event, WindowPtr ancestor)
|
|
{
|
|
int i;
|
|
WindowPtr pWin = NULL;
|
|
FocusClassPtr focus = IsPointerEvent((InternalEvent*)event) ? NULL : device->focus;
|
|
BOOL sendCore = (IsMaster(device) && device->coreEvents);
|
|
Bool ret = FALSE;
|
|
|
|
if (event->type != ET_ButtonPress &&
|
|
event->type != ET_KeyPress)
|
|
return FALSE;
|
|
|
|
if (event->type == ET_ButtonPress
|
|
&& (device->button->buttonsDown != 1))
|
|
return FALSE;
|
|
|
|
if (device->deviceGrab.grab)
|
|
return FALSE;
|
|
|
|
i = 0;
|
|
if (ancestor)
|
|
{
|
|
while (i < device->spriteInfo->sprite->spriteTraceGood)
|
|
if (device->spriteInfo->sprite->spriteTrace[i++] == ancestor)
|
|
break;
|
|
if (i == device->spriteInfo->sprite->spriteTraceGood)
|
|
goto out;
|
|
}
|
|
|
|
if (focus)
|
|
{
|
|
for (; i < focus->traceGood; i++)
|
|
{
|
|
pWin = focus->trace[i];
|
|
if (CheckPassiveGrabsOnWindow(pWin, device, (InternalEvent *) event,
|
|
sendCore, TRUE))
|
|
{
|
|
ret = TRUE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if ((focus->win == NoneWin) ||
|
|
(i >= device->spriteInfo->sprite->spriteTraceGood) ||
|
|
(pWin && pWin != device->spriteInfo->sprite->spriteTrace[i-1]))
|
|
goto out;
|
|
}
|
|
|
|
for (; i < device->spriteInfo->sprite->spriteTraceGood; i++)
|
|
{
|
|
pWin = device->spriteInfo->sprite->spriteTrace[i];
|
|
if (CheckPassiveGrabsOnWindow(pWin, device, (InternalEvent *) event,
|
|
sendCore, TRUE))
|
|
{
|
|
ret = TRUE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (ret == TRUE && event->type == ET_KeyPress)
|
|
device->deviceGrab.activatingKey = event->detail.key;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Called for keyboard events to deliver event to whatever client owns the
|
|
* focus.
|
|
*
|
|
* The 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 event The event, not yet in wire format.
|
|
* @param window Window underneath the sprite.
|
|
*/
|
|
void
|
|
DeliverFocusedEvent(DeviceIntPtr keybd, InternalEvent *event, WindowPtr window)
|
|
{
|
|
DeviceIntPtr ptr;
|
|
WindowPtr focus = keybd->focus->win;
|
|
BOOL sendCore = (IsMaster(keybd) && keybd->coreEvents);
|
|
xEvent *core = NULL, *xE = NULL, *xi2 = NULL;
|
|
int count, rc;
|
|
int deliveries = 0;
|
|
|
|
if (focus == FollowKeyboardWin)
|
|
focus = inputInfo.keyboard->focus->win;
|
|
if (!focus)
|
|
return;
|
|
if (focus == PointerRootWin)
|
|
{
|
|
DeliverDeviceEvents(window, event, NullGrab, NullWindow, keybd);
|
|
return;
|
|
}
|
|
if ((focus == window) || IsParent(focus, window))
|
|
{
|
|
if (DeliverDeviceEvents(window, event, NullGrab, focus, keybd))
|
|
return;
|
|
}
|
|
|
|
/* just deliver it to the focus window */
|
|
ptr = GetMaster(keybd, POINTER_OR_FLOAT);
|
|
|
|
rc = EventToXI2(event, &xi2);
|
|
if (rc == Success)
|
|
{
|
|
/* XXX: XACE */
|
|
int filter = GetEventFilter(keybd, xi2);
|
|
FixUpEventFromWindow(ptr->spriteInfo->sprite, xi2, focus, None, FALSE);
|
|
deliveries = DeliverEventsToWindow(keybd, focus, xi2, 1,
|
|
filter, NullGrab);
|
|
if (deliveries > 0)
|
|
goto unwind;
|
|
} else if (rc != BadMatch)
|
|
ErrorF("[dix] %s: XI2 conversion failed in DFE (%d, %d). Skipping delivery.\n",
|
|
keybd->name, event->any.type, rc);
|
|
|
|
rc = EventToXI(event, &xE, &count);
|
|
if (rc == Success &&
|
|
XaceHook(XACE_SEND_ACCESS, NULL, keybd, focus, xE, count) == Success)
|
|
{
|
|
FixUpEventFromWindow(ptr->spriteInfo->sprite, xE, focus, None, FALSE);
|
|
deliveries = DeliverEventsToWindow(keybd, focus, xE, count,
|
|
GetEventFilter(keybd, xE),
|
|
NullGrab);
|
|
|
|
if (deliveries > 0)
|
|
goto unwind;
|
|
} else if (rc != BadMatch)
|
|
ErrorF("[dix] %s: XI conversion failed in DFE (%d, %d). Skipping delivery.\n",
|
|
keybd->name, event->any.type, rc);
|
|
|
|
if (sendCore)
|
|
{
|
|
rc = EventToCore(event, &core, &count);
|
|
if (rc == Success) {
|
|
if (XaceHook(XACE_SEND_ACCESS, NULL, keybd, focus, core, count) == Success) {
|
|
FixUpEventFromWindow(keybd->spriteInfo->sprite, core, focus,
|
|
None, FALSE);
|
|
deliveries = DeliverEventsToWindow(keybd, focus, core, count,
|
|
GetEventFilter(keybd, core),
|
|
NullGrab);
|
|
}
|
|
} else if (rc != BadMatch)
|
|
ErrorF("[dix] %s: core conversion failed DFE (%d, %d). Skipping delivery.\n",
|
|
keybd->name, event->any.type, rc);
|
|
}
|
|
|
|
unwind:
|
|
free(core);
|
|
free(xE);
|
|
free(xi2);
|
|
return;
|
|
}
|
|
|
|
|
|
int
|
|
DeliverOneGrabbedEvent(InternalEvent *event, DeviceIntPtr dev, enum InputLevel level)
|
|
{
|
|
SpritePtr pSprite = dev->spriteInfo->sprite;
|
|
int rc;
|
|
xEvent *xE = NULL;
|
|
int count = 0;
|
|
int deliveries = 0;
|
|
Mask mask;
|
|
GrabInfoPtr grabinfo = &dev->deviceGrab;
|
|
GrabPtr grab = grabinfo->grab;
|
|
Mask filter;
|
|
|
|
switch(level)
|
|
{
|
|
case XI2:
|
|
rc = EventToXI2(event, &xE);
|
|
count = 1;
|
|
if (rc == Success)
|
|
{
|
|
int evtype = xi2_get_type(xE);
|
|
mask = xi2mask_isset(grab->xi2mask, dev, evtype);
|
|
filter = 1;
|
|
}
|
|
break;
|
|
case XI:
|
|
if (grabinfo->fromPassiveGrab && grabinfo->implicitGrab)
|
|
mask = grab->deviceMask;
|
|
else
|
|
mask = grab->eventMask;
|
|
rc = EventToXI(event, &xE, &count);
|
|
if (rc == Success)
|
|
filter = GetEventFilter(dev, xE);
|
|
break;
|
|
case CORE:
|
|
rc = EventToCore(event, &xE, &count);
|
|
mask = grab->eventMask;
|
|
if (rc == Success)
|
|
filter = GetEventFilter(dev, xE);
|
|
break;
|
|
default:
|
|
BUG_WARN_MSG(1, "Invalid input level %d\n", level);
|
|
return 0;
|
|
}
|
|
|
|
if (rc == Success)
|
|
{
|
|
FixUpEventFromWindow(pSprite, xE, grab->window, None, TRUE);
|
|
if (XaceHook(XACE_SEND_ACCESS, 0, dev,
|
|
grab->window, xE, count) ||
|
|
XaceHook(XACE_RECEIVE_ACCESS, rClient(grab),
|
|
grab->window, xE, count))
|
|
deliveries = 1; /* don't send, but pretend we did */
|
|
else if (level != CORE || !IsInterferingGrab(rClient(grab), dev, xE))
|
|
{
|
|
deliveries = TryClientEvents(rClient(grab), dev,
|
|
xE, count, mask, filter,
|
|
grab);
|
|
}
|
|
} else
|
|
BUG_WARN_MSG(rc != BadMatch, "%s: conversion to mode %d failed on %d with %d\n",
|
|
dev->name, level, event->any.type, rc);
|
|
|
|
free(xE);
|
|
return deliveries;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @return The number of events delivered.
|
|
*/
|
|
int
|
|
DeliverGrabbedEvent(InternalEvent *event, DeviceIntPtr thisDev,
|
|
Bool deactivateGrab)
|
|
{
|
|
GrabPtr grab;
|
|
GrabInfoPtr grabinfo;
|
|
int deliveries = 0;
|
|
DeviceIntPtr dev;
|
|
SpritePtr pSprite = thisDev->spriteInfo->sprite;
|
|
BOOL sendCore = FALSE;
|
|
|
|
grabinfo = &thisDev->deviceGrab;
|
|
grab = grabinfo->grab;
|
|
|
|
if (grab->ownerEvents)
|
|
{
|
|
WindowPtr focus;
|
|
WindowPtr win;
|
|
|
|
/* Hack: Some pointer device have a focus class. So we need to check
|
|
* for the type of event, to see if we really want to deliver it to
|
|
* the focus window. For pointer events, the answer is no.
|
|
*/
|
|
if (IsPointerEvent(event))
|
|
focus = PointerRootWin;
|
|
else if (thisDev->focus)
|
|
{
|
|
focus = thisDev->focus->win;
|
|
if (focus == FollowKeyboardWin)
|
|
focus = inputInfo.keyboard->focus->win;
|
|
}
|
|
else
|
|
focus = PointerRootWin;
|
|
if (focus == PointerRootWin)
|
|
{
|
|
win = pSprite->win;
|
|
focus = NullWindow;
|
|
} else if (focus && (focus == pSprite->win ||
|
|
IsParent(focus, pSprite->win)))
|
|
win = pSprite->win;
|
|
else if (focus)
|
|
win = focus;
|
|
|
|
deliveries = DeliverDeviceEvents(win, event, grab, focus, thisDev);
|
|
}
|
|
if (!deliveries)
|
|
{
|
|
/* XXX: In theory, we could pass the internal events through to
|
|
* everything and only convert just before hitting the wire. We can't
|
|
* do that yet, so DGE is the last stop for internal events. From here
|
|
* onwards, we deal with core/XI events.
|
|
*/
|
|
|
|
sendCore = (IsMaster(thisDev) && thisDev->coreEvents);
|
|
/* try core event */
|
|
if (sendCore && grab->grabtype == CORE)
|
|
{
|
|
deliveries = DeliverOneGrabbedEvent(event, thisDev, CORE);
|
|
}
|
|
|
|
if (!deliveries)
|
|
{
|
|
deliveries = DeliverOneGrabbedEvent(event, thisDev, XI2);
|
|
}
|
|
|
|
if (!deliveries)
|
|
{
|
|
deliveries = DeliverOneGrabbedEvent(event, thisDev, XI);
|
|
}
|
|
|
|
if (deliveries && (event->any.type == ET_Motion))
|
|
thisDev->valuator->motionHintWindow = grab->window;
|
|
}
|
|
if (deliveries && !deactivateGrab && event->any.type != ET_Motion)
|
|
{
|
|
switch (grabinfo->sync.state)
|
|
{
|
|
case FREEZE_BOTH_NEXT_EVENT:
|
|
dev = GetPairedDevice(thisDev);
|
|
if (dev)
|
|
{
|
|
FreezeThaw(dev, TRUE);
|
|
if ((dev->deviceGrab.sync.state == FREEZE_BOTH_NEXT_EVENT) &&
|
|
(CLIENT_BITS(grab->resource) ==
|
|
CLIENT_BITS(dev->deviceGrab.grab->resource)))
|
|
dev->deviceGrab.sync.state = FROZEN_NO_EVENT;
|
|
else
|
|
dev->deviceGrab.sync.other = grab;
|
|
}
|
|
/* fall through */
|
|
case FREEZE_NEXT_EVENT:
|
|
grabinfo->sync.state = FROZEN_WITH_EVENT;
|
|
FreezeThaw(thisDev, TRUE);
|
|
*grabinfo->sync.event = event->device_event;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return deliveries;
|
|
}
|
|
|
|
/* 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 (DeviceEvent *event, DeviceIntPtr keybd)
|
|
{
|
|
int key = event->detail.key;
|
|
|
|
if (event->type == ET_KeyPress) {
|
|
DebugF("FixKeyState: Key %d %s\n",key,
|
|
((event->type == ET_KeyPress) ? "down" : "up"));
|
|
}
|
|
|
|
if (event->type == ET_KeyPress)
|
|
set_key_down(keybd, key, KEY_PROCESSED);
|
|
else if (event->type == ET_KeyRelease)
|
|
set_key_up(keybd, key, KEY_PROCESSED);
|
|
else
|
|
FatalError("Impossible keyboard event");
|
|
}
|
|
|
|
#define AtMostOneClient \
|
|
(SubstructureRedirectMask | ResizeRedirectMask | ButtonPressMask)
|
|
#define ManagerMask \
|
|
(SubstructureRedirectMask | ResizeRedirectMask)
|
|
|
|
/**
|
|
* 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 plus the events that may be propagated to the parent.
|
|
*
|
|
* Traverses to siblings and parents of the window.
|
|
*/
|
|
void
|
|
RecalculateDeliverableEvents(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);
|
|
}
|
|
free(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;
|
|
DeviceIntPtr dev;
|
|
int rc;
|
|
|
|
if (mask & ~AllEventMasks)
|
|
{
|
|
client->errorValue = mask;
|
|
return BadValue;
|
|
}
|
|
check = (mask & ManagerMask);
|
|
if (check) {
|
|
rc = XaceHook(XACE_RESOURCE_ACCESS, client, pWin->drawable.id,
|
|
RT_WINDOW, pWin, RT_NONE, NULL, DixManageAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
}
|
|
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 = malloc(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 ((mask & PointerMotionHintMask) && !(check & PointerMotionHintMask))
|
|
{
|
|
for (dev = inputInfo.devices; dev; dev = dev->next)
|
|
{
|
|
if (dev->valuator && dev->valuator->motionHintWindow == pWin)
|
|
dev->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;
|
|
}
|
|
|
|
/**
|
|
* Assembles an EnterNotify or LeaveNotify and sends it event to the client.
|
|
* Uses the paired keyboard to get some additional information.
|
|
*/
|
|
void
|
|
CoreEnterLeaveEvent(
|
|
DeviceIntPtr mouse,
|
|
int type,
|
|
int mode,
|
|
int detail,
|
|
WindowPtr pWin,
|
|
Window child)
|
|
{
|
|
xEvent event;
|
|
WindowPtr focus;
|
|
DeviceIntPtr keybd;
|
|
GrabPtr grab = mouse->deviceGrab.grab;
|
|
Mask mask;
|
|
|
|
keybd = GetMaster(mouse, KEYBOARD_OR_FLOAT);
|
|
|
|
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);
|
|
}
|
|
|
|
memset(&event, 0, sizeof(xEvent));
|
|
event.u.u.type = type;
|
|
event.u.u.detail = detail;
|
|
event.u.enterLeave.time = currentTime.milliseconds;
|
|
event.u.enterLeave.rootX = mouse->spriteInfo->sprite->hot.x;
|
|
event.u.enterLeave.rootY = mouse->spriteInfo->sprite->hot.y;
|
|
/* Counts on the same initial structure of crossing & button events! */
|
|
FixUpEventFromWindow(mouse->spriteInfo->sprite, &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;
|
|
event.u.enterLeave.state = mouse->button ? (mouse->button->state & 0x1f00) : 0;
|
|
if (keybd)
|
|
event.u.enterLeave.state |=
|
|
XkbGrabStateFromRec(&keybd->key->xkbInfo->state);
|
|
event.u.enterLeave.mode = mode;
|
|
focus = (keybd) ? keybd->focus->win : None;
|
|
if ((focus != NoneWin) &&
|
|
((pWin == focus) || (focus == PointerRootWin) ||
|
|
IsParent(focus, pWin)))
|
|
event.u.enterLeave.flags |= ELFlagFocus;
|
|
|
|
if ((mask & GetEventFilter(mouse, &event)))
|
|
{
|
|
if (grab)
|
|
TryClientEvents(rClient(grab), mouse, &event, 1, mask,
|
|
GetEventFilter(mouse, &event), grab);
|
|
else
|
|
DeliverEventsToWindow(mouse, pWin, &event, 1,
|
|
GetEventFilter(mouse, &event),
|
|
NullGrab);
|
|
}
|
|
|
|
if ((type == EnterNotify) && (mask & KeymapStateMask))
|
|
{
|
|
xKeymapEvent ke;
|
|
ClientPtr client = grab ? rClient(grab) : wClient(pWin);
|
|
if (XaceHook(XACE_DEVICE_ACCESS, client, keybd, DixReadAccess))
|
|
memset((char *)&ke.map[0], 0, 31);
|
|
else
|
|
memmove((char *)&ke.map[0], (char *)&keybd->key->down[1], 31);
|
|
|
|
ke.type = KeymapNotify;
|
|
if (grab)
|
|
TryClientEvents(rClient(grab), keybd, (xEvent *)&ke, 1,
|
|
mask, KeymapStateMask, grab);
|
|
else
|
|
DeliverEventsToWindow(mouse, pWin, (xEvent *)&ke, 1,
|
|
KeymapStateMask, NullGrab);
|
|
}
|
|
}
|
|
|
|
void
|
|
DeviceEnterLeaveEvent(
|
|
DeviceIntPtr mouse,
|
|
int sourceid,
|
|
int type,
|
|
int mode,
|
|
int detail,
|
|
WindowPtr pWin,
|
|
Window child)
|
|
{
|
|
GrabPtr grab = mouse->deviceGrab.grab;
|
|
xXIEnterEvent *event;
|
|
int filter;
|
|
int btlen, len, i;
|
|
DeviceIntPtr kbd;
|
|
|
|
if ((mode == XINotifyPassiveGrab && type == XI_Leave) ||
|
|
(mode == XINotifyPassiveUngrab && type == XI_Enter))
|
|
return;
|
|
|
|
btlen = (mouse->button) ? bits_to_bytes(mouse->button->numButtons) : 0;
|
|
btlen = bytes_to_int32(btlen);
|
|
len = sizeof(xXIEnterEvent) + btlen * 4;
|
|
|
|
event = calloc(1, len);
|
|
event->type = GenericEvent;
|
|
event->extension = IReqCode;
|
|
event->evtype = type;
|
|
event->length = (len - sizeof(xEvent))/4;
|
|
event->buttons_len = btlen;
|
|
event->detail = detail;
|
|
event->time = currentTime.milliseconds;
|
|
event->deviceid = mouse->id;
|
|
event->sourceid = sourceid;
|
|
event->mode = mode;
|
|
event->root_x = FP1616(mouse->spriteInfo->sprite->hot.x, 0);
|
|
event->root_y = FP1616(mouse->spriteInfo->sprite->hot.y, 0);
|
|
|
|
for (i = 0; mouse && mouse->button && i < mouse->button->numButtons; i++)
|
|
if (BitIsOn(mouse->button->down, i))
|
|
SetBit(&event[1], i);
|
|
|
|
kbd = GetMaster(mouse, MASTER_KEYBOARD);
|
|
if (kbd && kbd->key)
|
|
{
|
|
event->mods.base_mods = kbd->key->xkbInfo->state.base_mods;
|
|
event->mods.latched_mods = kbd->key->xkbInfo->state.latched_mods;
|
|
event->mods.locked_mods = kbd->key->xkbInfo->state.locked_mods;
|
|
|
|
event->group.base_group = kbd->key->xkbInfo->state.base_group;
|
|
event->group.latched_group = kbd->key->xkbInfo->state.latched_group;
|
|
event->group.locked_group = kbd->key->xkbInfo->state.locked_group;
|
|
}
|
|
|
|
FixUpEventFromWindow(mouse->spriteInfo->sprite, (xEvent*)event, pWin,
|
|
None, FALSE);
|
|
|
|
filter = GetEventFilter(mouse, (xEvent*)event);
|
|
|
|
if (grab)
|
|
{
|
|
Mask mask;
|
|
mask = xi2mask_isset(grab->xi2mask, mouse, type);
|
|
TryClientEvents(rClient(grab), mouse, (xEvent*)event, 1, mask, 1, grab);
|
|
} else {
|
|
if (!WindowXI2MaskIsset(mouse, pWin, (xEvent*)event))
|
|
goto out;
|
|
DeliverEventsToWindow(mouse, pWin, (xEvent*)event, 1, filter,
|
|
NullGrab);
|
|
}
|
|
|
|
out:
|
|
free(event);
|
|
}
|
|
|
|
void
|
|
CoreFocusEvent(DeviceIntPtr dev, int type, int mode, int detail, WindowPtr pWin)
|
|
{
|
|
xEvent event;
|
|
|
|
memset(&event, 0, sizeof(xEvent));
|
|
event.u.focus.mode = mode;
|
|
event.u.u.type = type;
|
|
event.u.u.detail = detail;
|
|
event.u.focus.window = pWin->drawable.id;
|
|
|
|
DeliverEventsToWindow(dev, pWin, &event, 1,
|
|
GetEventFilter(dev, &event), NullGrab);
|
|
if ((type == FocusIn) &&
|
|
((pWin->eventMask | wOtherEventMasks(pWin)) & KeymapStateMask))
|
|
{
|
|
xKeymapEvent ke;
|
|
ClientPtr client = wClient(pWin);
|
|
if (XaceHook(XACE_DEVICE_ACCESS, client, dev, DixReadAccess))
|
|
memset((char *)&ke.map[0], 0, 31);
|
|
else
|
|
memmove((char *)&ke.map[0], (char *)&dev->key->down[1], 31);
|
|
|
|
ke.type = KeymapNotify;
|
|
DeliverEventsToWindow(dev, pWin, (xEvent *)&ke, 1,
|
|
KeymapStateMask, NullGrab);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
DeviceIntPtr keybd; /* used for FollowKeyboard or FollowKeyboardWin */
|
|
|
|
|
|
UpdateCurrentTime();
|
|
if ((revertTo != RevertToParent) &&
|
|
(revertTo != RevertToPointerRoot) &&
|
|
(revertTo != RevertToNone) &&
|
|
((revertTo != RevertToFollowKeyboard) || !followOK))
|
|
{
|
|
client->errorValue = revertTo;
|
|
return BadValue;
|
|
}
|
|
time = ClientTimeToServerTime(ctime);
|
|
|
|
keybd = GetMaster(dev, KEYBOARD_OR_FLOAT);
|
|
|
|
if ((focusID == None) || (focusID == PointerRoot))
|
|
focusWin = (WindowPtr)(long)focusID;
|
|
else if ((focusID == FollowKeyboard) && followOK)
|
|
{
|
|
focusWin = keybd->focus->win;
|
|
}
|
|
else {
|
|
rc = dixLookupWindow(&focusWin, focusID, client, DixSetAttrAccess);
|
|
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;
|
|
}
|
|
rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixSetFocusAccess);
|
|
if (rc != Success)
|
|
return Success;
|
|
|
|
focus = dev->focus;
|
|
if ((CompareTimeStamps(time, currentTime) == LATER) ||
|
|
(CompareTimeStamps(time, focus->time) == EARLIER))
|
|
return Success;
|
|
mode = (dev->deviceGrab.grab) ? NotifyWhileGrabbed : NotifyNormal;
|
|
if (focus->win == FollowKeyboardWin)
|
|
{
|
|
if (!ActivateFocusInGrab(dev, keybd->focus->win, focusWin))
|
|
DoFocusEvents(dev, keybd->focus->win, focusWin, mode);
|
|
} else
|
|
{
|
|
if (!ActivateFocusInGrab(dev, focus->win, focusWin))
|
|
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;
|
|
focus->trace = realloc(focus->trace,
|
|
focus->traceSize * sizeof(WindowPtr));
|
|
}
|
|
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(ClientPtr client)
|
|
{
|
|
DeviceIntPtr kbd = PickKeyboard(client);
|
|
REQUEST(xSetInputFocusReq);
|
|
|
|
REQUEST_SIZE_MATCH(xSetInputFocusReq);
|
|
|
|
return SetInputFocus(client, kbd, stuff->focus,
|
|
stuff->revertTo, stuff->time, FALSE);
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for GetInputFocus request.
|
|
*
|
|
* Sends the current input focus for the client's keyboard back to the
|
|
* client.
|
|
*/
|
|
int
|
|
ProcGetInputFocus(ClientPtr client)
|
|
{
|
|
DeviceIntPtr kbd = PickKeyboard(client);
|
|
xGetInputFocusReply rep;
|
|
FocusClassPtr focus = kbd->focus;
|
|
int rc;
|
|
/* REQUEST(xReq); */
|
|
REQUEST_SIZE_MATCH(xReq);
|
|
|
|
rc = XaceHook(XACE_DEVICE_ACCESS, client, kbd, DixGetFocusAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
memset(&rep, 0, sizeof(xGetInputFocusReply));
|
|
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 client's ClientPointer and returns success
|
|
* status to client.
|
|
*/
|
|
int
|
|
ProcGrabPointer(ClientPtr client)
|
|
{
|
|
xGrabPointerReply rep;
|
|
DeviceIntPtr device = PickPointer(client);
|
|
GrabPtr grab;
|
|
GrabMask mask;
|
|
WindowPtr confineTo;
|
|
CursorPtr oldCursor;
|
|
REQUEST(xGrabPointerReq);
|
|
int rc;
|
|
|
|
REQUEST_SIZE_MATCH(xGrabPointerReq);
|
|
UpdateCurrentTime();
|
|
|
|
if (stuff->eventMask & ~PointerGrabMask)
|
|
{
|
|
client->errorValue = stuff->eventMask;
|
|
return BadValue;
|
|
}
|
|
|
|
if (stuff->confineTo == None)
|
|
confineTo = NullWindow;
|
|
else
|
|
{
|
|
rc = dixLookupWindow(&confineTo, stuff->confineTo, client,
|
|
DixSetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
}
|
|
|
|
memset(&rep, 0, sizeof(xGrabPointerReply));
|
|
oldCursor = NullCursor;
|
|
grab = device->deviceGrab.grab;
|
|
|
|
if (grab)
|
|
{
|
|
if (grab->confineTo && !confineTo)
|
|
ConfineCursorToWindow(device, GetCurrentRootWindow(device), FALSE,
|
|
FALSE);
|
|
oldCursor = grab->cursor;
|
|
}
|
|
|
|
mask.core = stuff->eventMask;
|
|
|
|
rc = GrabDevice(client, device, stuff->pointerMode, stuff->keyboardMode,
|
|
stuff->grabWindow, stuff->ownerEvents, stuff->time,
|
|
&mask, CORE, stuff->cursor,
|
|
stuff->confineTo, &rep.status);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
if (oldCursor && rep.status == GrabSuccess)
|
|
FreeCursor (oldCursor, (Cursor)0);
|
|
|
|
rep.type = X_Reply;
|
|
rep.sequenceNumber = client->sequence;
|
|
rep.length = 0;
|
|
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.
|
|
*/
|
|
int
|
|
ProcChangeActivePointerGrab(ClientPtr client)
|
|
{
|
|
DeviceIntPtr device;
|
|
GrabPtr 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
|
|
{
|
|
int rc = dixLookupResourceByType((pointer *)&newCursor, stuff->cursor,
|
|
RT_CURSOR, client, DixUseAccess);
|
|
if (rc != Success)
|
|
{
|
|
client->errorValue = stuff->cursor;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
device = PickPointer(client);
|
|
grab = device->deviceGrab.grab;
|
|
|
|
if (!grab)
|
|
return Success;
|
|
if (!SameClient(grab, client))
|
|
return Success;
|
|
time = ClientTimeToServerTime(stuff->time);
|
|
if ((CompareTimeStamps(time, currentTime) == LATER) ||
|
|
(CompareTimeStamps(time, device->deviceGrab.grabTime) == EARLIER))
|
|
return Success;
|
|
oldCursor = grab->cursor;
|
|
grab->cursor = newCursor;
|
|
if (newCursor)
|
|
newCursor->refcnt++;
|
|
PostNewCursor(device);
|
|
if (oldCursor)
|
|
FreeCursor(oldCursor, (Cursor)0);
|
|
grab->eventMask = stuff->eventMask;
|
|
return Success;
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for UngrabPointer request.
|
|
*
|
|
* Deletes a pointer grab on a device the client has grabbed.
|
|
*/
|
|
int
|
|
ProcUngrabPointer(ClientPtr client)
|
|
{
|
|
DeviceIntPtr device = PickPointer(client);
|
|
GrabPtr grab;
|
|
TimeStamp time;
|
|
REQUEST(xResourceReq);
|
|
|
|
REQUEST_SIZE_MATCH(xResourceReq);
|
|
UpdateCurrentTime();
|
|
grab = device->deviceGrab.grab;
|
|
|
|
time = ClientTimeToServerTime(stuff->id);
|
|
if ((CompareTimeStamps(time, currentTime) != LATER) &&
|
|
(CompareTimeStamps(time, device->deviceGrab.grabTime) != EARLIER) &&
|
|
(grab) && SameClient(grab, client))
|
|
(*device->deviceGrab.DeactivateGrab)(device);
|
|
return Success;
|
|
}
|
|
|
|
/**
|
|
* Sets a grab on the given device.
|
|
*
|
|
* Called from ProcGrabKeyboard to work on the client's 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 pointer_mode, unsigned keyboard_mode, Window grabWindow,
|
|
unsigned ownerEvents, Time ctime, GrabMask *mask,
|
|
int grabtype, Cursor curs, Window confineToWin, CARD8 *status)
|
|
{
|
|
WindowPtr pWin, confineTo;
|
|
GrabPtr grab;
|
|
TimeStamp time;
|
|
Mask access_mode = DixGrabAccess;
|
|
int rc;
|
|
GrabInfoPtr grabInfo = &dev->deviceGrab;
|
|
CursorPtr cursor;
|
|
|
|
UpdateCurrentTime();
|
|
if ((keyboard_mode != GrabModeSync) && (keyboard_mode != GrabModeAsync))
|
|
{
|
|
client->errorValue = keyboard_mode;
|
|
return BadValue;
|
|
}
|
|
if ((pointer_mode != GrabModeSync) && (pointer_mode != GrabModeAsync))
|
|
{
|
|
client->errorValue = pointer_mode;
|
|
return BadValue;
|
|
}
|
|
if ((ownerEvents != xFalse) && (ownerEvents != xTrue))
|
|
{
|
|
client->errorValue = ownerEvents;
|
|
return BadValue;
|
|
}
|
|
|
|
rc = dixLookupWindow(&pWin, grabWindow, client, DixSetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
if (confineToWin == None)
|
|
confineTo = NullWindow;
|
|
else
|
|
{
|
|
rc = dixLookupWindow(&confineTo, confineToWin, client,
|
|
DixSetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
}
|
|
|
|
if (curs == None)
|
|
cursor = NullCursor;
|
|
else
|
|
{
|
|
rc = dixLookupResourceByType((pointer *)&cursor, curs, RT_CURSOR,
|
|
client, DixUseAccess);
|
|
if (rc != Success)
|
|
{
|
|
client->errorValue = curs;
|
|
return rc;
|
|
}
|
|
access_mode |= DixForceAccess;
|
|
}
|
|
|
|
if (keyboard_mode == GrabModeSync || pointer_mode == GrabModeSync)
|
|
access_mode |= DixFreezeAccess;
|
|
rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, access_mode);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
time = ClientTimeToServerTime(ctime);
|
|
grab = grabInfo->grab;
|
|
if (grab && grab->grabtype != grabtype)
|
|
*status = AlreadyGrabbed;
|
|
if (grab && !SameClient(grab, client))
|
|
*status = AlreadyGrabbed;
|
|
else if ((!pWin->realized) ||
|
|
(confineTo &&
|
|
!(confineTo->realized
|
|
&& BorderSizeNotEmpty(dev, confineTo))))
|
|
*status = GrabNotViewable;
|
|
else if ((CompareTimeStamps(time, currentTime) == LATER) ||
|
|
(CompareTimeStamps(time, grabInfo->grabTime) == EARLIER))
|
|
*status = GrabInvalidTime;
|
|
else if (grabInfo->sync.frozen &&
|
|
grabInfo->sync.other && !SameClient(grabInfo->sync.other, client))
|
|
*status = GrabFrozen;
|
|
else
|
|
{
|
|
GrabPtr tempGrab;
|
|
|
|
tempGrab = AllocGrab();
|
|
|
|
tempGrab->next = NULL;
|
|
tempGrab->window = pWin;
|
|
tempGrab->resource = client->clientAsMask;
|
|
tempGrab->ownerEvents = ownerEvents;
|
|
tempGrab->keyboardMode = keyboard_mode;
|
|
tempGrab->pointerMode = pointer_mode;
|
|
if (grabtype == CORE)
|
|
tempGrab->eventMask = mask->core;
|
|
else if (grabtype == XI)
|
|
tempGrab->eventMask = mask->xi;
|
|
else
|
|
xi2mask_merge(tempGrab->xi2mask, mask->xi2mask);
|
|
tempGrab->device = dev;
|
|
tempGrab->cursor = cursor;
|
|
tempGrab->confineTo = confineTo;
|
|
tempGrab->grabtype = grabtype;
|
|
(*grabInfo->ActivateGrab)(dev, tempGrab, time, FALSE);
|
|
*status = GrabSuccess;
|
|
|
|
FreeGrab(tempGrab);
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for GrabKeyboard request.
|
|
*
|
|
* Grabs the client's keyboard and returns success status to client.
|
|
*/
|
|
int
|
|
ProcGrabKeyboard(ClientPtr client)
|
|
{
|
|
xGrabKeyboardReply rep;
|
|
REQUEST(xGrabKeyboardReq);
|
|
int result;
|
|
DeviceIntPtr keyboard = PickKeyboard(client);
|
|
GrabMask mask;
|
|
|
|
REQUEST_SIZE_MATCH(xGrabKeyboardReq);
|
|
|
|
memset(&rep, 0, sizeof(xGrabKeyboardReply));
|
|
mask.core = KeyPressMask | KeyReleaseMask;
|
|
|
|
result = GrabDevice(client, keyboard, stuff->pointerMode,
|
|
stuff->keyboardMode, stuff->grabWindow, stuff->ownerEvents,
|
|
stuff->time, &mask, CORE, None, None,
|
|
&rep.status);
|
|
|
|
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 client's keyboard.
|
|
*/
|
|
int
|
|
ProcUngrabKeyboard(ClientPtr client)
|
|
{
|
|
DeviceIntPtr device = PickKeyboard(client);
|
|
GrabPtr grab;
|
|
TimeStamp time;
|
|
REQUEST(xResourceReq);
|
|
|
|
REQUEST_SIZE_MATCH(xResourceReq);
|
|
UpdateCurrentTime();
|
|
|
|
grab = device->deviceGrab.grab;
|
|
|
|
time = ClientTimeToServerTime(stuff->id);
|
|
if ((CompareTimeStamps(time, currentTime) != LATER) &&
|
|
(CompareTimeStamps(time, device->deviceGrab.grabTime) != EARLIER) &&
|
|
(grab) && SameClient(grab, client) && grab->grabtype == CORE)
|
|
(*device->deviceGrab.DeactivateGrab)(device);
|
|
return Success;
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for QueryPointer request.
|
|
*
|
|
* Returns the current state and position of the client's ClientPointer to the
|
|
* client.
|
|
*/
|
|
int
|
|
ProcQueryPointer(ClientPtr client)
|
|
{
|
|
xQueryPointerReply rep;
|
|
WindowPtr pWin, t;
|
|
DeviceIntPtr mouse = PickPointer(client);
|
|
DeviceIntPtr keyboard;
|
|
SpritePtr pSprite;
|
|
int rc;
|
|
REQUEST(xResourceReq);
|
|
REQUEST_SIZE_MATCH(xResourceReq);
|
|
|
|
rc = dixLookupWindow(&pWin, stuff->id, client, DixGetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
rc = XaceHook(XACE_DEVICE_ACCESS, client, mouse, DixReadAccess);
|
|
if (rc != Success && rc != BadAccess)
|
|
return rc;
|
|
|
|
keyboard = GetMaster(mouse, MASTER_KEYBOARD);
|
|
|
|
pSprite = mouse->spriteInfo->sprite;
|
|
if (mouse->valuator->motionHintWindow)
|
|
MaybeStopHint(mouse, client);
|
|
memset(&rep, 0, sizeof(xQueryPointerReply));
|
|
rep.type = X_Reply;
|
|
rep.sequenceNumber = client->sequence;
|
|
rep.mask = mouse->button ? (mouse->button->state) : 0;
|
|
rep.mask |= XkbStateFieldFromRec(&keyboard->key->xkbInfo->state);
|
|
rep.length = 0;
|
|
rep.root = (GetCurrentRootWindow(mouse))->drawable.id;
|
|
rep.rootX = pSprite->hot.x;
|
|
rep.rootY = pSprite->hot.y;
|
|
rep.child = None;
|
|
if (pSprite->hot.pScreen == pWin->drawable.pScreen)
|
|
{
|
|
rep.sameScreen = xTrue;
|
|
rep.winX = pSprite->hot.x - pWin->drawable.x;
|
|
rep.winY = pSprite->hot.y - pWin->drawable.y;
|
|
for (t = pSprite->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 += screenInfo.screens[0]->x;
|
|
rep.rootY += screenInfo.screens[0]->y;
|
|
if(stuff->id == rep.root) {
|
|
rep.winX += screenInfo.screens[0]->x;
|
|
rep.winY += screenInfo.screens[0]->y;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (rc == BadAccess) {
|
|
rep.mask = 0;
|
|
rep.child = None;
|
|
rep.rootX = 0;
|
|
rep.rootY = 0;
|
|
rep.winX = 0;
|
|
rep.winY = 0;
|
|
}
|
|
|
|
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;
|
|
QdEventPtr qe, tmp;
|
|
|
|
inputInfo.numDevices = 0;
|
|
inputInfo.devices = (DeviceIntPtr)NULL;
|
|
inputInfo.off_devices = (DeviceIntPtr)NULL;
|
|
inputInfo.keyboard = (DeviceIntPtr)NULL;
|
|
inputInfo.pointer = (DeviceIntPtr)NULL;
|
|
for (i = 0; i < MAXDEVICES; i++)
|
|
{
|
|
memcpy(&event_filters[i], default_filter, sizeof(default_filter));
|
|
}
|
|
|
|
syncEvents.replayDev = (DeviceIntPtr)NULL;
|
|
syncEvents.replayWin = NullWindow;
|
|
if (syncEvents.pending.next)
|
|
list_for_each_entry_safe(qe, tmp, &syncEvents.pending, next)
|
|
free(qe);
|
|
list_init(&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;
|
|
}
|
|
|
|
InputEventList = InitEventList(GetMaximumEventsNum());
|
|
if (!InputEventList)
|
|
FatalError("[dix] Failed to allocate input event list.\n");
|
|
}
|
|
|
|
void
|
|
CloseDownEvents(void)
|
|
{
|
|
FreeEventList(InputEventList, GetMaximumEventsNum());
|
|
InputEventList = NULL;
|
|
}
|
|
|
|
#define SEND_EVENT_BIT 0x80
|
|
|
|
/**
|
|
* 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 */
|
|
DeviceIntPtr dev = PickPointer(client);
|
|
DeviceIntPtr keybd = GetMaster(dev, MASTER_KEYBOARD);
|
|
SpritePtr pSprite = dev->spriteInfo->sprite;
|
|
REQUEST(xSendEventReq);
|
|
|
|
REQUEST_SIZE_MATCH(xSendEventReq);
|
|
|
|
/* libXext and other extension libraries may set the bit indicating
|
|
* that this event came from a SendEvent request so remove it
|
|
* since otherwise the event type may fail the range checks
|
|
* and cause an invalid BadValue error to be returned.
|
|
*
|
|
* This is safe to do since we later add the SendEvent bit (0x80)
|
|
* back in once we send the event to the client */
|
|
|
|
stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
|
|
|
|
/* 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 = pSprite->win;
|
|
else if (stuff->destination == InputFocus)
|
|
{
|
|
WindowPtr inputFocus = (keybd) ? keybd->focus->win : NoneWin;
|
|
|
|
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 = GetCurrentRootWindow(dev);
|
|
|
|
if (IsParent(inputFocus, pSprite->win))
|
|
{
|
|
effectiveFocus = inputFocus;
|
|
pWin = pSprite->win;
|
|
}
|
|
else
|
|
effectiveFocus = pWin = inputFocus;
|
|
}
|
|
else
|
|
dixLookupWindow(&pWin, stuff->destination, client, DixSendAccess);
|
|
|
|
if (!pWin)
|
|
return BadWindow;
|
|
if ((stuff->propagate != xFalse) && (stuff->propagate != xTrue))
|
|
{
|
|
client->errorValue = stuff->propagate;
|
|
return BadValue;
|
|
}
|
|
stuff->event.u.u.type |= SEND_EVENT_BIT;
|
|
if (stuff->propagate)
|
|
{
|
|
for (;pWin; pWin = pWin->parent)
|
|
{
|
|
if (XaceHook(XACE_SEND_ACCESS, client, NULL, pWin,
|
|
&stuff->event, 1))
|
|
return Success;
|
|
if (DeliverEventsToWindow(dev, pWin,
|
|
&stuff->event, 1, stuff->eventMask, NullGrab))
|
|
return Success;
|
|
if (pWin == effectiveFocus)
|
|
return Success;
|
|
stuff->eventMask &= ~wDontPropagateMask(pWin);
|
|
if (!stuff->eventMask)
|
|
break;
|
|
}
|
|
}
|
|
else if (!XaceHook(XACE_SEND_ACCESS, client, NULL, pWin, &stuff->event, 1))
|
|
DeliverEventsToWindow(dev, pWin, &stuff->event,
|
|
1, stuff->eventMask, NullGrab);
|
|
return Success;
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for UngrabKey request.
|
|
*
|
|
* Deletes a passive grab for the given key. Works on the
|
|
* client's keyboard.
|
|
*/
|
|
int
|
|
ProcUngrabKey(ClientPtr client)
|
|
{
|
|
REQUEST(xUngrabKeyReq);
|
|
WindowPtr pWin;
|
|
GrabPtr tempGrab;
|
|
DeviceIntPtr keybd = PickKeyboard(client);
|
|
int rc;
|
|
|
|
REQUEST_SIZE_MATCH(xUngrabKeyReq);
|
|
rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixGetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
if (((stuff->key > keybd->key->xkbInfo->desc->max_key_code) ||
|
|
(stuff->key < keybd->key->xkbInfo->desc->min_key_code))
|
|
&& (stuff->key != AnyKey))
|
|
{
|
|
client->errorValue = stuff->key;
|
|
return BadValue;
|
|
}
|
|
if ((stuff->modifiers != AnyModifier) &&
|
|
(stuff->modifiers & ~AllModifiersMask))
|
|
{
|
|
client->errorValue = stuff->modifiers;
|
|
return BadValue;
|
|
}
|
|
tempGrab = AllocGrab();
|
|
if (!tempGrab)
|
|
return BadAlloc;
|
|
tempGrab->resource = client->clientAsMask;
|
|
tempGrab->device = keybd;
|
|
tempGrab->window = pWin;
|
|
tempGrab->modifiersDetail.exact = stuff->modifiers;
|
|
tempGrab->modifiersDetail.pMask = NULL;
|
|
tempGrab->modifierDevice = keybd;
|
|
tempGrab->type = KeyPress;
|
|
tempGrab->grabtype = CORE;
|
|
tempGrab->detail.exact = stuff->key;
|
|
tempGrab->detail.pMask = NULL;
|
|
tempGrab->next = NULL;
|
|
|
|
if (!DeletePassiveGrabFromList(tempGrab))
|
|
rc = BadAlloc;
|
|
|
|
FreeGrab(tempGrab);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for GrabKey request.
|
|
*
|
|
* Creates a grab for the client's keyboard and adds it to the list of passive
|
|
* grabs.
|
|
*/
|
|
int
|
|
ProcGrabKey(ClientPtr client)
|
|
{
|
|
WindowPtr pWin;
|
|
REQUEST(xGrabKeyReq);
|
|
GrabPtr grab;
|
|
DeviceIntPtr keybd = PickKeyboard(client);
|
|
int rc;
|
|
GrabParameters param;
|
|
GrabMask mask;
|
|
|
|
REQUEST_SIZE_MATCH(xGrabKeyReq);
|
|
|
|
memset(¶m, 0, sizeof(param));
|
|
param.grabtype = CORE;
|
|
param.ownerEvents = stuff->ownerEvents;
|
|
param.this_device_mode = stuff->keyboardMode;
|
|
param.other_devices_mode = stuff->pointerMode;
|
|
param.modifiers = stuff->modifiers;
|
|
|
|
rc = CheckGrabValues(client, ¶m);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
if (((stuff->key > keybd->key->xkbInfo->desc->max_key_code) ||
|
|
(stuff->key < keybd->key->xkbInfo->desc->min_key_code))
|
|
&& (stuff->key != AnyKey))
|
|
{
|
|
client->errorValue = stuff->key;
|
|
return BadValue;
|
|
}
|
|
rc = dixLookupWindow(&pWin, stuff->grabWindow, client, DixSetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
|
|
mask.core = (KeyPressMask | KeyReleaseMask);
|
|
|
|
grab = CreateGrab(client->index, keybd, keybd, pWin, CORE, &mask,
|
|
¶m, KeyPress, stuff->key, NullWindow, NullCursor);
|
|
if (!grab)
|
|
return BadAlloc;
|
|
return AddPassiveGrabToList(client, grab);
|
|
}
|
|
|
|
|
|
/**
|
|
* Server-side protocol handling for GrabButton request.
|
|
*
|
|
* Creates a grab for the client's ClientPointer and adds it as a passive grab
|
|
* to the list.
|
|
*/
|
|
int
|
|
ProcGrabButton(ClientPtr client)
|
|
{
|
|
WindowPtr pWin, confineTo;
|
|
REQUEST(xGrabButtonReq);
|
|
CursorPtr cursor;
|
|
GrabPtr grab;
|
|
DeviceIntPtr ptr, modifierDevice;
|
|
Mask access_mode = DixGrabAccess;
|
|
GrabMask mask;
|
|
GrabParameters param;
|
|
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, DixSetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
if (stuff->confineTo == None)
|
|
confineTo = NullWindow;
|
|
else {
|
|
rc = dixLookupWindow(&confineTo, stuff->confineTo, client,
|
|
DixSetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
}
|
|
if (stuff->cursor == None)
|
|
cursor = NullCursor;
|
|
else
|
|
{
|
|
rc = dixLookupResourceByType((pointer *)&cursor, stuff->cursor, RT_CURSOR,
|
|
client, DixUseAccess);
|
|
if (rc != Success)
|
|
{
|
|
client->errorValue = stuff->cursor;
|
|
return rc;
|
|
}
|
|
access_mode |= DixForceAccess;
|
|
}
|
|
|
|
ptr = PickPointer(client);
|
|
modifierDevice = GetMaster(ptr, MASTER_KEYBOARD);
|
|
if (stuff->pointerMode == GrabModeSync ||
|
|
stuff->keyboardMode == GrabModeSync)
|
|
access_mode |= DixFreezeAccess;
|
|
rc = XaceHook(XACE_DEVICE_ACCESS, client, ptr, access_mode);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
memset(¶m, 0, sizeof(param));
|
|
param.grabtype = CORE;
|
|
param.ownerEvents = stuff->ownerEvents;
|
|
param.this_device_mode = stuff->keyboardMode;
|
|
param.other_devices_mode = stuff->pointerMode;
|
|
param.modifiers = stuff->modifiers;
|
|
|
|
mask.core = stuff->eventMask;
|
|
|
|
grab = CreateGrab(client->index, ptr, modifierDevice, pWin,
|
|
CORE, &mask, ¶m, ButtonPress,
|
|
stuff->button, confineTo, cursor);
|
|
if (!grab)
|
|
return BadAlloc;
|
|
return AddPassiveGrabToList(client, grab);
|
|
}
|
|
|
|
/**
|
|
* Server-side protocol handling for UngrabButton request.
|
|
*
|
|
* Deletes a passive grab on the client's ClientPointer from the list.
|
|
*/
|
|
int
|
|
ProcUngrabButton(ClientPtr client)
|
|
{
|
|
REQUEST(xUngrabButtonReq);
|
|
WindowPtr pWin;
|
|
GrabPtr tempGrab;
|
|
int rc;
|
|
DeviceIntPtr ptr;
|
|
|
|
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;
|
|
|
|
ptr = PickPointer(client);
|
|
|
|
tempGrab = AllocGrab();
|
|
if (!tempGrab)
|
|
return BadAlloc;
|
|
tempGrab->resource = client->clientAsMask;
|
|
tempGrab->device = ptr;
|
|
tempGrab->window = pWin;
|
|
tempGrab->modifiersDetail.exact = stuff->modifiers;
|
|
tempGrab->modifiersDetail.pMask = NULL;
|
|
tempGrab->modifierDevice = GetMaster(ptr, MASTER_KEYBOARD);
|
|
tempGrab->type = ButtonPress;
|
|
tempGrab->detail.exact = stuff->button;
|
|
tempGrab->grabtype = CORE;
|
|
tempGrab->detail.pMask = NULL;
|
|
tempGrab->next = NULL;
|
|
|
|
if (!DeletePassiveGrabFromList(tempGrab))
|
|
rc = BadAlloc;
|
|
|
|
FreeGrab(tempGrab);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
OtherClientsPtr oc;
|
|
GrabPtr passive;
|
|
GrabPtr grab;
|
|
|
|
|
|
/* Deactivate any grabs performed on this window, before making any
|
|
input focus changes. */
|
|
grab = mouse->deviceGrab.grab;
|
|
if (grab &&
|
|
((grab->window == pWin) || (grab->confineTo == pWin)))
|
|
(*mouse->deviceGrab.DeactivateGrab)(mouse);
|
|
|
|
|
|
/* Deactivating a keyboard grab should cause focus events. */
|
|
grab = keybd->deviceGrab.grab;
|
|
if (grab && (grab->window == pWin))
|
|
(*keybd->deviceGrab.DeactivateGrab)(keybd);
|
|
|
|
/* And now the real devices */
|
|
for (mouse = inputInfo.devices; mouse; mouse = mouse->next)
|
|
{
|
|
grab = mouse->deviceGrab.grab;
|
|
if (grab && ((grab->window == pWin) || (grab->confineTo == pWin)))
|
|
(*mouse->deviceGrab.DeactivateGrab)(mouse);
|
|
}
|
|
|
|
|
|
for (keybd = inputInfo.devices; keybd; keybd = keybd->next)
|
|
{
|
|
if (IsKeyboardDevice(keybd))
|
|
{
|
|
focus = keybd->focus;
|
|
|
|
/* 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->deviceGrab.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
|
|
|| wClient(parent)->clientGone
|
|
#endif
|
|
);
|
|
if (!ActivateFocusInGrab(keybd, pWin, parent))
|
|
DoFocusEvents(keybd, pWin, parent, focusEventMode);
|
|
focus->win = parent;
|
|
focus->revert = RevertToNone;
|
|
break;
|
|
case RevertToPointerRoot:
|
|
if (!ActivateFocusInGrab(keybd, pWin, PointerRootWin))
|
|
DoFocusEvents(keybd, pWin, PointerRootWin, focusEventMode);
|
|
focus->win = PointerRootWin;
|
|
focus->traceGood = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsPointerDevice(keybd))
|
|
{
|
|
if (keybd->valuator->motionHintWindow == pWin)
|
|
keybd->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);
|
|
}
|
|
|
|
DeleteWindowFromAnyExtEvents(pWin, freeResources);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void
|
|
CheckCursorConfinement(WindowPtr pWin)
|
|
{
|
|
GrabPtr grab;
|
|
WindowPtr confineTo;
|
|
DeviceIntPtr pDev;
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension && pWin->drawable.pScreen->myNum) return;
|
|
#endif
|
|
|
|
for (pDev = inputInfo.devices; pDev; pDev = pDev->next)
|
|
{
|
|
if (DevHasCursor(pDev))
|
|
{
|
|
grab = pDev->deviceGrab.grab;
|
|
if (grab && (confineTo = grab->confineTo))
|
|
{
|
|
if (!BorderSizeNotEmpty(pDev, confineTo))
|
|
(*pDev->deviceGrab.DeactivateGrab)(pDev);
|
|
else if ((pWin == confineTo) || IsParent(pWin, confineTo))
|
|
ConfineCursorToWindow(pDev, 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 rc, nscr;
|
|
ScreenPtr pscr;
|
|
Bool displayed;
|
|
SpritePtr pSprite = PickPointer(client)->spriteInfo->sprite;
|
|
REQUEST(xRecolorCursorReq);
|
|
|
|
REQUEST_SIZE_MATCH(xRecolorCursorReq);
|
|
rc = dixLookupResourceByType((pointer *)&pCursor, stuff->cursor, RT_CURSOR,
|
|
client, DixWriteAccess);
|
|
if (rc != Success)
|
|
{
|
|
client->errorValue = stuff->cursor;
|
|
return rc;
|
|
}
|
|
|
|
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 == pSprite->screen);
|
|
else
|
|
#endif
|
|
displayed = (pscr == pSprite->hotPhys.pScreen);
|
|
( *pscr->RecolorCursor)(PickPointer(client), pscr, pCursor,
|
|
(pCursor == pSprite->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.
|
|
*/
|
|
void
|
|
WriteEventsToClient(ClientPtr pClient, int count, xEvent *events)
|
|
{
|
|
#ifdef PANORAMIX
|
|
xEvent eventCopy;
|
|
#endif
|
|
xEvent *eventTo, *eventFrom;
|
|
int i,
|
|
eventlength = sizeof(xEvent);
|
|
|
|
if (!pClient || pClient == serverClient || pClient->clientGone)
|
|
return;
|
|
|
|
for (i = 0; i < count; i++)
|
|
if ((events[i].u.u.type & 0x7f) != KeymapNotify)
|
|
events[i].u.u.sequenceNumber = pClient->sequence;
|
|
|
|
/* Let XKB rewrite the state, as it depends on client preferences. */
|
|
XkbFilterEvents(pClient, count, events);
|
|
|
|
#ifdef PANORAMIX
|
|
if(!noPanoramiXExtension &&
|
|
(screenInfo.screens[0]->x || screenInfo.screens[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 += screenInfo.screens[0]->x;
|
|
eventCopy.u.keyButtonPointer.rootY += screenInfo.screens[0]->y;
|
|
if(eventCopy.u.keyButtonPointer.event ==
|
|
eventCopy.u.keyButtonPointer.root)
|
|
{
|
|
eventCopy.u.keyButtonPointer.eventX += screenInfo.screens[0]->x;
|
|
eventCopy.u.keyButtonPointer.eventY += screenInfo.screens[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
|
|
/* Just a safety check to make sure we only have one GenericEvent, it just
|
|
* makes things easier for me right now. (whot) */
|
|
for (i = 1; i < count; i++)
|
|
{
|
|
if (events[i].u.u.type == GenericEvent)
|
|
{
|
|
ErrorF("[dix] TryClientEvents: Only one GenericEvent at a time.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (events->u.u.type == GenericEvent)
|
|
{
|
|
eventlength += ((xGenericEvent*)events)->length * 4;
|
|
}
|
|
|
|
if(pClient->swapped)
|
|
{
|
|
if (eventlength > swapEventLen)
|
|
{
|
|
swapEventLen = eventlength;
|
|
swapEvent = realloc(swapEvent, swapEventLen);
|
|
if (!swapEvent)
|
|
{
|
|
FatalError("WriteEventsToClient: Out of memory.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < count; i++)
|
|
{
|
|
eventFrom = &events[i];
|
|
eventTo = swapEvent;
|
|
|
|
/* 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);
|
|
|
|
WriteToClient(pClient, eventlength, (char *)eventTo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* only one GenericEvent, remember? that means either count is 1 and
|
|
* eventlength is arbitrary or eventlength is 32 and count doesn't
|
|
* matter. And we're all set. Woohoo. */
|
|
WriteToClient(pClient, count * eventlength, (char *) events);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the client pointer for the given client.
|
|
*
|
|
* A client can have exactly one ClientPointer. Each time a
|
|
* request/reply/event is processed and the choice of devices is ambiguous
|
|
* (e.g. QueryPointer request), the server will pick the ClientPointer (see
|
|
* PickPointer()).
|
|
* If a keyboard is needed, the first keyboard paired with the CP is used.
|
|
*/
|
|
int
|
|
SetClientPointer(ClientPtr client, DeviceIntPtr device)
|
|
{
|
|
int rc = XaceHook(XACE_DEVICE_ACCESS, client, device, DixUseAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
if (!IsMaster(device))
|
|
{
|
|
ErrorF("[dix] Need master device for ClientPointer. This is a bug.\n");
|
|
return BadDevice;
|
|
} else if (!device->spriteInfo->spriteOwner)
|
|
{
|
|
ErrorF("[dix] Device %d does not have a sprite. "
|
|
"Cannot be ClientPointer\n", device->id);
|
|
return BadDevice;
|
|
}
|
|
client->clientPtr = device;
|
|
return Success;
|
|
}
|
|
|
|
/* PickPointer will pick an appropriate pointer for the given client.
|
|
*
|
|
* An "appropriate device" is (in order of priority):
|
|
* 1) A device the given client has a core grab on.
|
|
* 2) A device set as ClientPointer for the given client.
|
|
* 3) The first master device.
|
|
*/
|
|
DeviceIntPtr
|
|
PickPointer(ClientPtr client)
|
|
{
|
|
DeviceIntPtr it = inputInfo.devices;
|
|
|
|
/* First, check if the client currently has a grab on a device. Even
|
|
* keyboards count. */
|
|
for(it = inputInfo.devices; it; it = it->next)
|
|
{
|
|
GrabPtr grab = it->deviceGrab.grab;
|
|
if (grab && grab->grabtype == CORE && SameClient(grab, client))
|
|
{
|
|
it = GetMaster(it, MASTER_POINTER);
|
|
return it; /* Always return a core grabbed device */
|
|
}
|
|
}
|
|
|
|
if (!client->clientPtr)
|
|
{
|
|
it = inputInfo.devices;
|
|
while (it)
|
|
{
|
|
if (IsMaster(it) && it->spriteInfo->spriteOwner)
|
|
{
|
|
client->clientPtr = it;
|
|
break;
|
|
}
|
|
it = it->next;
|
|
}
|
|
}
|
|
return client->clientPtr;
|
|
}
|
|
|
|
/* PickKeyboard will pick an appropriate keyboard for the given client by
|
|
* searching the list of devices for the keyboard device that is paired with
|
|
* the client's pointer.
|
|
*/
|
|
DeviceIntPtr
|
|
PickKeyboard(ClientPtr client)
|
|
{
|
|
DeviceIntPtr ptr = PickPointer(client);
|
|
DeviceIntPtr kbd = GetMaster(ptr, MASTER_KEYBOARD);
|
|
|
|
if (!kbd)
|
|
{
|
|
ErrorF("[dix] ClientPointer not paired with a keyboard. This "
|
|
"is a bug.\n");
|
|
}
|
|
|
|
return kbd;
|
|
}
|
|
|
|
/* A client that has one or more core grabs does not get core events from
|
|
* devices it does not have a grab on. Legacy applications behave bad
|
|
* otherwise because they are not used to it and the events interfere.
|
|
* Only applies for core events.
|
|
*
|
|
* Return true if a core event from the device would interfere and should not
|
|
* be delivered.
|
|
*/
|
|
Bool
|
|
IsInterferingGrab(ClientPtr client, DeviceIntPtr dev, xEvent* event)
|
|
{
|
|
DeviceIntPtr it = inputInfo.devices;
|
|
|
|
switch(event->u.u.type)
|
|
{
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
case MotionNotify:
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if (dev->deviceGrab.grab && SameClient(dev->deviceGrab.grab, client))
|
|
return FALSE;
|
|
|
|
while(it)
|
|
{
|
|
if (it != dev)
|
|
{
|
|
if (it->deviceGrab.grab && SameClient(it->deviceGrab.grab, client)
|
|
&& !it->deviceGrab.fromPassiveGrab)
|
|
{
|
|
if ((IsPointerDevice(it) && IsPointerDevice(dev)) ||
|
|
(IsKeyboardDevice(it) && IsKeyboardDevice(dev)))
|
|
return TRUE;
|
|
}
|
|
}
|
|
it = it->next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|