TinyX/dix/events.c

3436 lines
109 KiB
C
Raw Permalink Normal View History

2013-11-30 08:51:10 -05:00
/************************************************************
Copyright 1987, 1998 The Open Group
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Digital not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
********************************************************/
/* The panoramix components contained the following notice */
/*****************************************************************
Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Digital Equipment Corporation
shall not be used in advertising or otherwise to promote the sale, use or other
dealings in this Software without prior written authorization from Digital
Equipment Corporation.
******************************************************************/
/*****************************************************************
Copyright 2003-2005 Sun Microsystems, Inc.
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, provided that the above
copyright notice(s) and this permission notice appear in all copies of
the Software and that both the above copyright notice(s) and this
permission notice appear in supporting documentation.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale, use
or other dealings in this Software without prior written authorization
of the copyright holder.
******************************************************************/
#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 "scrnintstr.h"
#include "cursorstr.h"
#include "dixstruct.h"
#include "globals.h"
#include <X11/extensions/XIproto.h>
#include "exevents.h"
#include "extnsionst.h"
#include "dixevents.h"
#include "dixgrabs.h"
#include "dispatch.h"
#define EXTENSION_EVENT_BASE 64
#define NoSuchEvent 0x80000000 /* so doesn't match NoEventMask */
#define StructureAndSubMask ( StructureNotifyMask | SubstructureNotifyMask )
#define AllButtonsMask ( \
Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
#define MotionMask ( \
PointerMotionMask | Button1MotionMask | \
Button2MotionMask | Button3MotionMask | Button4MotionMask | \
Button5MotionMask | ButtonMotionMask )
#define PropagateMask ( \
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | \
MotionMask )
#define PointerGrabMask ( \
ButtonPressMask | ButtonReleaseMask | \
EnterWindowMask | LeaveWindowMask | \
PointerMotionHintMask | KeymapStateMask | \
MotionMask )
#define AllModifiersMask ( \
ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \
Mod3Mask | Mod4Mask | Mod5Mask )
#define AllEventMasks (lastEventMask|(lastEventMask-1))
/*
* The following relies on the fact that the Button<n>MotionMasks are equal
* to the corresponding Button<n>Masks from the current modifier/button state.
*/
#define Motion_Filter(class) (PointerMotionMask | \
(class)->state | (class)->motionMask)
#define WID(w) ((w) ? ((w)->drawable.id) : 0)
#define XE_KBPTR (xE->u.keyButtonPointer)
#define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
_X_EXPORT CallbackListPtr EventCallback;
_X_EXPORT CallbackListPtr DeviceEventCallback;
#define DNPMCOUNT 8
Mask DontPropagateMasks[DNPMCOUNT];
static int DontPropagateRefCnts[DNPMCOUNT];
#ifdef DEBUG
static debug_events = 0;
#endif
_X_EXPORT InputInfo inputInfo;
static struct {
QdEventPtr pending, *pendtail;
DeviceIntPtr replayDev; /* kludgy rock to put flag for */
WindowPtr replayWin; /* ComputeFreezes */
Bool playingEvents;
TimeStamp time;
} syncEvents;
/*
* The window trace information is used to avoid having to compute all the
* windows between the root and the current pointer window each time a button
* or key goes down. The grabs on each of those windows must be checked.
*/
static WindowPtr *spriteTrace = (WindowPtr *) NULL;
#define ROOT spriteTrace[0]
static int spriteTraceSize = 0;
static int spriteTraceGood;
static struct {
CursorPtr current;
BoxRec hotLimits; /* logical constraints of hot spot */
Bool confined; /* confined to screen */
RegionPtr hotShape; /* additional logical shape constraint */
BoxRec physLimits; /* physical constraints of hot spot */
WindowPtr win; /* window of logical position */
HotSpot hot; /* logical pointer position */
HotSpot hotPhys; /* physical pointer position */
} sprite; /* info about the cursor sprite */
static void DoEnterLeaveEvents(WindowPtr fromWin, WindowPtr toWin, int mode);
static WindowPtr XYToWindow(int x, int y);
extern int lastEvent;
static Mask lastEventMask;
#define CantBeFiltered NoEventMask
static Mask filters[128];
static const Mask initialFilters[128] = {
NoSuchEvent, /* 0 */
NoSuchEvent, /* 1 */
KeyPressMask, /* KeyPress */
KeyReleaseMask, /* KeyRelease */
ButtonPressMask, /* ButtonPress */
ButtonReleaseMask, /* ButtonRelease */
PointerMotionMask, /* MotionNotify (initial state) */
EnterWindowMask, /* EnterNotify */
LeaveWindowMask, /* LeaveNotify */
FocusChangeMask, /* FocusIn */
FocusChangeMask, /* FocusOut */
KeymapStateMask, /* KeymapNotify */
ExposureMask, /* Expose */
CantBeFiltered, /* GraphicsExpose */
CantBeFiltered, /* NoExpose */
VisibilityChangeMask, /* VisibilityNotify */
SubstructureNotifyMask, /* CreateNotify */
StructureAndSubMask, /* DestroyNotify */
StructureAndSubMask, /* UnmapNotify */
StructureAndSubMask, /* MapNotify */
SubstructureRedirectMask, /* MapRequest */
StructureAndSubMask, /* ReparentNotify */
StructureAndSubMask, /* ConfigureNotify */
SubstructureRedirectMask, /* ConfigureRequest */
StructureAndSubMask, /* GravityNotify */
ResizeRedirectMask, /* ResizeRequest */
StructureAndSubMask, /* CirculateNotify */
SubstructureRedirectMask, /* CirculateRequest */
PropertyChangeMask, /* PropertyNotify */
CantBeFiltered, /* SelectionClear */
CantBeFiltered, /* SelectionRequest */
CantBeFiltered, /* SelectionNotify */
ColormapChangeMask, /* ColormapNotify */
CantBeFiltered, /* ClientMessage */
CantBeFiltered /* MappingNotify */
};
static CARD8 criticalEvents[32] = {
0x7c /* key and button events */
};
void
SetMaskForEvent(Mask mask, int event)
{
if ((event < LASTEvent) || (event >= 128))
FatalError("SetMaskForEvent: bogus event number");
filters[event] = mask;
}
_X_EXPORT void
SetCriticalEvent(int event)
{
if (event >= 128)
FatalError("SetCriticalEvent: bogus event number");
criticalEvents[event >> 3] |= 1 << (event & 7);
}
static void
SyntheticMotion(int x, int y)
{
xEvent xE;
xE.u.keyButtonPointer.rootX = x;
xE.u.keyButtonPointer.rootY = y;
if (syncEvents.playingEvents)
xE.u.keyButtonPointer.time = syncEvents.time.milliseconds;
else
xE.u.keyButtonPointer.time = currentTime.milliseconds;
xE.u.u.type = MotionNotify;
(*inputInfo.pointer->public.processInputProc) (&xE, inputInfo.pointer, 1);
}
static void
ConfineToShape(RegionPtr shape, int *px, int *py)
{
BoxRec box;
int x = *px, y = *py;
int incx = 1, incy = 1;
if (POINT_IN_REGION(shape, x, y, &box))
return;
box = *REGION_EXTENTS(shape);
/* this is rather crude */
do {
x += incx;
if (x >= box.x2) {
incx = -1;
x = *px - 1;
}
else if (x < box.x1) {
incx = 1;
x = *px;
y += incy;
if (y >= box.y2) {
incy = -1;
y = *py - 1;
}
else if (y < box.y1)
return; /* should never get here! */
}
} while (!POINT_IN_REGION(shape, x, y, &box));
*px = x;
*py = y;
}
static void
CheckPhysLimits(CursorPtr cursor,
Bool generateEvents, Bool confineToScreen, ScreenPtr pScreen)
{
HotSpot new;
if (!cursor)
return;
new = sprite.hotPhys;
if (pScreen)
new.pScreen = pScreen;
else
pScreen = new.pScreen;
(*pScreen->CursorLimits) (pScreen, cursor, &sprite.hotLimits,
&sprite.physLimits);
sprite.confined = confineToScreen;
(*pScreen->ConstrainCursor) (pScreen, &sprite.physLimits);
if (new.x < sprite.physLimits.x1)
new.x = sprite.physLimits.x1;
else if (new.x >= sprite.physLimits.x2)
new.x = sprite.physLimits.x2 - 1;
if (new.y < sprite.physLimits.y1)
new.y = sprite.physLimits.y1;
else if (new.y >= sprite.physLimits.y2)
new.y = sprite.physLimits.y2 - 1;
if (sprite.hotShape)
ConfineToShape(sprite.hotShape, &new.x, &new.y);
if ((pScreen != sprite.hotPhys.pScreen) ||
(new.x != sprite.hotPhys.x) || (new.y != sprite.hotPhys.y)) {
if (pScreen != sprite.hotPhys.pScreen)
sprite.hotPhys = new;
(*pScreen->SetCursorPosition) (pScreen, new.x, new.y, generateEvents);
if (!generateEvents)
SyntheticMotion(new.x, new.y);
}
}
static void
CheckVirtualMotion(register QdEventPtr qe, register WindowPtr pWin)
{
if (qe) {
sprite.hot.pScreen = qe->pScreen;
sprite.hot.x = qe->event->u.keyButtonPointer.rootX;
sprite.hot.y = qe->event->u.keyButtonPointer.rootY;
pWin = inputInfo.pointer->grab ? inputInfo.pointer->grab->confineTo :
NullWindow;
}
if (pWin) {
BoxRec lims;
if (sprite.hot.pScreen != pWin->drawable.pScreen) {
sprite.hot.pScreen = pWin->drawable.pScreen;
sprite.hot.x = sprite.hot.y = 0;
}
lims = *REGION_EXTENTS(&pWin->borderSize);
if (sprite.hot.x < lims.x1)
sprite.hot.x = lims.x1;
else if (sprite.hot.x >= lims.x2)
sprite.hot.x = lims.x2 - 1;
if (sprite.hot.y < lims.y1)
sprite.hot.y = lims.y1;
else if (sprite.hot.y >= lims.y2)
sprite.hot.y = lims.y2 - 1;
if (wBoundingShape(pWin))
ConfineToShape(&pWin->borderSize, &sprite.hot.x, &sprite.hot.y);
if (qe) {
qe->pScreen = sprite.hot.pScreen;
qe->event->u.keyButtonPointer.rootX = sprite.hot.x;
qe->event->u.keyButtonPointer.rootY = sprite.hot.y;
}
}
ROOT = WindowTable[sprite.hot.pScreen->myNum];
}
static void
ConfineCursorToWindow(WindowPtr pWin, Bool generateEvents, Bool confineToScreen)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
if (syncEvents.playingEvents) {
CheckVirtualMotion((QdEventPtr) NULL, pWin);
SyntheticMotion(sprite.hot.x, sprite.hot.y);
}
else {
sprite.hotLimits = *REGION_EXTENTS(&pWin->borderSize);
sprite.hotShape = wBoundingShape(pWin) ? &pWin->borderSize : NullRegion;
CheckPhysLimits(sprite.current, generateEvents, confineToScreen,
pScreen);
}
}
_X_EXPORT Bool
PointerConfinedToScreen()
{
return sprite.confined;
}
static void
ChangeToCursor(CursorPtr cursor)
{
if (cursor != sprite.current) {
if ((sprite.current->bits->xhot != cursor->bits->xhot) ||
(sprite.current->bits->yhot != cursor->bits->yhot))
CheckPhysLimits(cursor, FALSE, sprite.confined, (ScreenPtr) NULL);
(*sprite.hotPhys.pScreen->DisplayCursor) (sprite.hotPhys.pScreen,
cursor);
FreeCursor(sprite.current, (Cursor) 0);
sprite.current = cursor;
sprite.current->refcnt++;
}
}
/* returns true if b is a descendent of a */
Bool
IsParent(register WindowPtr a, register WindowPtr b)
{
for (b = b->parent; b; b = b->parent)
if (b == a)
return TRUE;
return FALSE;
}
static void
PostNewCursor(void)
{
WindowPtr win;
GrabPtr grab = inputInfo.pointer->grab;
if (syncEvents.playingEvents)
return;
if (grab) {
if (grab->cursor) {
ChangeToCursor(grab->cursor);
return;
}
if (IsParent(grab->window, sprite.win))
win = sprite.win;
else
win = grab->window;
}
else
win = sprite.win;
for (; win; win = win->parent)
if (win->optional && win->optional->cursor != NullCursor) {
ChangeToCursor(win->optional->cursor);
return;
}
}
_X_EXPORT WindowPtr
GetCurrentRootWindow()
{
return ROOT;
}
_X_EXPORT WindowPtr
GetSpriteWindow()
{
return sprite.win;
}
_X_EXPORT CursorPtr
GetSpriteCursor()
{
return sprite.current;
}
_X_EXPORT void
GetSpritePosition(int *px, int *py)
{
*px = sprite.hotPhys.x;
*py = sprite.hotPhys.y;
}
#define TIMESLOP (5 * 60 * 1000) /* 5 minutes */
static void
MonthChangedOrBadTime(register xEvent *xE)
{
/* If the ddx/OS is careless about not processing timestamped events from
* different sources in sorted order, then it's possible for time to go
* backwards when it should not. Here we ensure a decent time.
*/
if ((currentTime.milliseconds - XE_KBPTR.time) > TIMESLOP)
currentTime.months++;
else
XE_KBPTR.time = currentTime.milliseconds;
}
#define NoticeTime(xE) { \
if ((xE)->u.keyButtonPointer.time < currentTime.milliseconds) \
MonthChangedOrBadTime(xE); \
currentTime.milliseconds = (xE)->u.keyButtonPointer.time; \
lastDeviceEventTime = currentTime; }
void
NoticeEventTime(register xEvent *xE)
{
if (!syncEvents.playingEvents)
NoticeTime(xE);
}
/**************************************************************************
* The following procedures deal with synchronous events *
**************************************************************************/
void
EnqueueEvent(xEvent *xE, DeviceIntPtr device, int count)
{
QdEventPtr tail = *syncEvents.pendtail;
QdEventPtr qe;
xEvent *qxE;
NoticeTime(xE);
if (DeviceEventCallback) {
DeviceEventInfoRec eventinfo;
/* The RECORD spec says that the root window field of motion events
* must be valid. At this point, it hasn't been filled in yet, so
* we do it here. The long expression below is necessary to get
* the current root window; the apparently reasonable alternative
* GetCurrentRootWindow()->drawable.id doesn't give you the right
* answer on the first motion event after a screen change because
* the data that GetCurrentRootWindow relies on hasn't been
* updated yet.
*/
if (xE->u.u.type == MotionNotify)
XE_KBPTR.root =
WindowTable[sprite.hotPhys.pScreen->myNum]->drawable.id;
eventinfo.events = xE;
eventinfo.count = count;
CallCallbacks(&DeviceEventCallback, (pointer) &eventinfo);
}
if (xE->u.u.type == MotionNotify) {
sprite.hotPhys.x = XE_KBPTR.rootX;
sprite.hotPhys.y = XE_KBPTR.rootY;
/* do motion compression */
if (tail &&
(tail->event->u.u.type == MotionNotify) &&
(tail->pScreen == sprite.hotPhys.pScreen)) {
tail->event->u.keyButtonPointer.rootX = sprite.hotPhys.x;
tail->event->u.keyButtonPointer.rootY = sprite.hotPhys.y;
tail->event->u.keyButtonPointer.time = XE_KBPTR.time;
tail->months = currentTime.months;
return;
}
}
qe = malloc(sizeof(QdEventRec) + (count * sizeof(xEvent)));
if (!qe)
return;
qe->next = (QdEventPtr) NULL;
qe->device = device;
qe->pScreen = sprite.hotPhys.pScreen;
qe->months = currentTime.months;
qe->event = (xEvent *) (qe + 1);
qe->evcount = count;
for (qxE = qe->event; --count >= 0; qxE++, xE++)
*qxE = *xE;
if (tail)
syncEvents.pendtail = &tail->next;
*syncEvents.pendtail = qe;
}
static void
PlayReleasedEvents(void)
{
QdEventPtr *prev, qe;
DeviceIntPtr dev;
prev = &syncEvents.pending;
while ((qe = *prev)) {
if (!qe->device->sync.frozen) {
*prev = qe->next;
if (*syncEvents.pendtail == *prev)
syncEvents.pendtail = prev;
if (qe->event->u.u.type == MotionNotify)
CheckVirtualMotion(qe, NullWindow);
syncEvents.time.months = qe->months;
syncEvents.time.milliseconds = qe->event->u.keyButtonPointer.time;
(*qe->device->public.processInputProc) (qe->event, qe->device,
qe->evcount);
free(qe);
for (dev = inputInfo.devices; dev && dev->sync.frozen;
dev = dev->next);
if (!dev)
break;
/* Playing the event may have unfrozen another device. */
/* So to play it safe, restart at the head of the queue */
prev = &syncEvents.pending;
}
else
prev = &qe->next;
}
}
static void
FreezeThaw(register DeviceIntPtr dev, Bool frozen)
{
dev->sync.frozen = frozen;
if (frozen)
dev->public.processInputProc = dev->public.enqueueInputProc;
else
dev->public.processInputProc = dev->public.realInputProc;
}
void
ComputeFreezes()
{
DeviceIntPtr replayDev = syncEvents.replayDev;
int i;
WindowPtr w;
xEvent *xE;
int count;
GrabPtr grab;
DeviceIntPtr dev;
for (dev = inputInfo.devices; dev; dev = dev->next)
FreezeThaw(dev, dev->sync.other || (dev->sync.state >= FROZEN));
if (syncEvents.playingEvents || (!replayDev && !syncEvents.pending))
return;
syncEvents.playingEvents = TRUE;
if (replayDev) {
xE = replayDev->sync.event;
count = replayDev->sync.evcount;
syncEvents.replayDev = (DeviceIntPtr) NULL;
w = XYToWindow(XE_KBPTR.rootX, XE_KBPTR.rootY);
for (i = 0; i < spriteTraceGood; i++) {
if (syncEvents.replayWin == spriteTrace[i]) {
if (!CheckDeviceGrabs(replayDev, xE, i + 1, count)) {
if (replayDev->focus)
DeliverFocusedEvent(replayDev, xE, w, count);
else
DeliverDeviceEvents(w, xE, NullGrab, NullWindow,
replayDev, count);
}
goto playmore;
}
}
/* must not still be in the same stack */
if (replayDev->focus)
DeliverFocusedEvent(replayDev, xE, w, count);
else
DeliverDeviceEvents(w, xE, NullGrab, NullWindow, replayDev, count);
}
playmore:
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (!dev->sync.frozen) {
PlayReleasedEvents();
break;
}
}
syncEvents.playingEvents = FALSE;
/* the following may have been skipped during replay, so do it now */
if ((grab = inputInfo.pointer->grab) &&grab->confineTo) {
if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen)
sprite.hotPhys.x = sprite.hotPhys.y = 0;
ConfineCursorToWindow(grab->confineTo, TRUE, TRUE);
}
else
ConfineCursorToWindow(WindowTable[sprite.hotPhys.pScreen->myNum],
TRUE, FALSE);
PostNewCursor();
}
void
ScreenRestructured(ScreenPtr pScreen)
{
GrabPtr grab;
if ((grab = inputInfo.pointer->grab) &&grab->confineTo) {
if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen)
sprite.hotPhys.x = sprite.hotPhys.y = 0;
ConfineCursorToWindow(grab->confineTo, TRUE, TRUE);
}
else
ConfineCursorToWindow(WindowTable[sprite.hotPhys.pScreen->myNum],
TRUE, FALSE);
}
void
CheckGrabForSyncs(register DeviceIntPtr thisDev, Bool thisMode, Bool otherMode)
{
GrabPtr grab = thisDev->grab;
DeviceIntPtr dev;
if (thisMode == GrabModeSync)
thisDev->sync.state = FROZEN_NO_EVENT;
else { /* free both if same client owns both */
thisDev->sync.state = THAWED;
if (thisDev->sync.other &&
(CLIENT_BITS(thisDev->sync.other->resource) ==
CLIENT_BITS(grab->resource)))
thisDev->sync.other = NullGrab;
}
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev != thisDev) {
if (otherMode == GrabModeSync)
dev->sync.other = grab;
else { /* free both if same client owns both */
if (dev->sync.other &&
(CLIENT_BITS(dev->sync.other->resource) ==
CLIENT_BITS(grab->resource)))
dev->sync.other = NullGrab;
}
}
}
ComputeFreezes();
}
void
ActivatePointerGrab(register DeviceIntPtr mouse, register GrabPtr grab,
TimeStamp time, Bool autoGrab)
{
WindowPtr oldWin = (mouse->grab) ? mouse->grab->window : sprite.win;
if (grab->confineTo) {
if (grab->confineTo->drawable.pScreen != sprite.hotPhys.pScreen)
sprite.hotPhys.x = sprite.hotPhys.y = 0;
ConfineCursorToWindow(grab->confineTo, FALSE, TRUE);
}
DoEnterLeaveEvents(oldWin, grab->window, NotifyGrab);
mouse->valuator->motionHintWindow = NullWindow;
if (syncEvents.playingEvents)
mouse->grabTime = syncEvents.time;
else
mouse->grabTime = time;
if (grab->cursor)
grab->cursor->refcnt++;
mouse->activeGrab = *grab;
mouse->grab = &mouse->activeGrab;
mouse->fromPassiveGrab = autoGrab;
PostNewCursor();
CheckGrabForSyncs(mouse, (Bool) grab->pointerMode,
(Bool) grab->keyboardMode);
}
void
DeactivatePointerGrab(register DeviceIntPtr mouse)
{
GrabPtr grab = mouse->grab;
DeviceIntPtr dev;
mouse->valuator->motionHintWindow = NullWindow;
mouse->grab = NullGrab;
mouse->sync.state = NOT_GRABBED;
mouse->fromPassiveGrab = FALSE;
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev->sync.other == grab)
dev->sync.other = NullGrab;
}
DoEnterLeaveEvents(grab->window, sprite.win, NotifyUngrab);
if (grab->confineTo)
ConfineCursorToWindow(ROOT, FALSE, FALSE);
PostNewCursor();
if (grab->cursor)
FreeCursor(grab->cursor, (Cursor) 0);
ComputeFreezes();
}
void
ActivateKeyboardGrab(register DeviceIntPtr keybd, GrabPtr grab, TimeStamp time,
Bool passive)
{
WindowPtr oldWin;
if (keybd->grab)
oldWin = keybd->grab->window;
else if (keybd->focus)
oldWin = keybd->focus->win;
else
oldWin = sprite.win;
if (oldWin == FollowKeyboardWin)
oldWin = inputInfo.keyboard->focus->win;
if (keybd->valuator)
keybd->valuator->motionHintWindow = NullWindow;
DoFocusEvents(keybd, oldWin, grab->window, NotifyGrab);
if (syncEvents.playingEvents)
keybd->grabTime = syncEvents.time;
else
keybd->grabTime = time;
keybd->activeGrab = *grab;
keybd->grab = &keybd->activeGrab;
keybd->fromPassiveGrab = passive;
CheckGrabForSyncs(keybd, (Bool) grab->keyboardMode,
(Bool) grab->pointerMode);
}
void
DeactivateKeyboardGrab(register DeviceIntPtr keybd)
{
GrabPtr grab = keybd->grab;
DeviceIntPtr dev;
WindowPtr focusWin = keybd->focus ? keybd->focus->win : sprite.win;
if (focusWin == FollowKeyboardWin)
focusWin = inputInfo.keyboard->focus->win;
if (keybd->valuator)
keybd->valuator->motionHintWindow = NullWindow;
keybd->grab = NullGrab;
keybd->sync.state = NOT_GRABBED;
keybd->fromPassiveGrab = FALSE;
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev->sync.other == grab)
dev->sync.other = NullGrab;
}
DoFocusEvents(keybd, grab->window, focusWin, NotifyUngrab);
ComputeFreezes();
}
void
AllowSome(ClientPtr client, TimeStamp time, DeviceIntPtr thisDev, int newState)
{
Bool thisGrabbed, otherGrabbed, othersFrozen, thisSynced;
TimeStamp grabTime;
DeviceIntPtr dev;
thisGrabbed = thisDev->grab && SameClient(thisDev->grab, client);
thisSynced = FALSE;
otherGrabbed = FALSE;
othersFrozen = TRUE;
grabTime = thisDev->grabTime;
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev == thisDev)
continue;
if (dev->grab && SameClient(dev->grab, client)) {
if (!(thisGrabbed || otherGrabbed) ||
(CompareTimeStamps(dev->grabTime, grabTime) == LATER))
grabTime = dev->grabTime;
otherGrabbed = TRUE;
if (thisDev->sync.other == dev->grab)
thisSynced = TRUE;
if (dev->sync.state < FROZEN)
othersFrozen = FALSE;
}
else if (!dev->sync.other || !SameClient(dev->sync.other, client))
othersFrozen = FALSE;
}
if (!((thisGrabbed && thisDev->sync.state >= FROZEN) || thisSynced))
return;
if ((CompareTimeStamps(time, currentTime) == LATER) ||
(CompareTimeStamps(time, grabTime) == EARLIER))
return;
switch (newState) {
case THAWED: /* Async */
if (thisGrabbed)
thisDev->sync.state = THAWED;
if (thisSynced)
thisDev->sync.other = NullGrab;
ComputeFreezes();
break;
case FREEZE_NEXT_EVENT: /* Sync */
if (thisGrabbed) {
thisDev->sync.state = FREEZE_NEXT_EVENT;
if (thisSynced)
thisDev->sync.other = NullGrab;
ComputeFreezes();
}
break;
case THAWED_BOTH: /* AsyncBoth */
if (othersFrozen) {
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev->grab && SameClient(dev->grab, client))
dev->sync.state = THAWED;
if (dev->sync.other && SameClient(dev->sync.other, client))
dev->sync.other = NullGrab;
}
ComputeFreezes();
}
break;
case FREEZE_BOTH_NEXT_EVENT: /* SyncBoth */
if (othersFrozen) {
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev->grab && SameClient(dev->grab, client))
dev->sync.state = FREEZE_BOTH_NEXT_EVENT;
if (dev->sync.other && SameClient(dev->sync.other, client))
dev->sync.other = NullGrab;
}
ComputeFreezes();
}
break;
case NOT_GRABBED: /* Replay */
if (thisGrabbed && thisDev->sync.state == FROZEN_WITH_EVENT) {
if (thisSynced)
thisDev->sync.other = NullGrab;
syncEvents.replayDev = thisDev;
syncEvents.replayWin = thisDev->grab->window;
(*thisDev->DeactivateGrab) (thisDev);
syncEvents.replayDev = (DeviceIntPtr) NULL;
}
break;
case THAW_OTHERS: /* AsyncOthers */
if (othersFrozen) {
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev == thisDev)
continue;
if (dev->grab && SameClient(dev->grab, client))
dev->sync.state = THAWED;
if (dev->sync.other && SameClient(dev->sync.other, client))
dev->sync.other = NullGrab;
}
ComputeFreezes();
}
break;
}
}
int
ProcAllowEvents(register ClientPtr client)
{
TimeStamp time;
DeviceIntPtr mouse = inputInfo.pointer;
DeviceIntPtr keybd = inputInfo.keyboard;
REQUEST(xAllowEventsReq);
REQUEST_SIZE_MATCH(xAllowEventsReq);
time = ClientTimeToServerTime(stuff->time);
switch (stuff->mode) {
case ReplayPointer:
AllowSome(client, time, mouse, NOT_GRABBED);
break;
case SyncPointer:
AllowSome(client, time, mouse, FREEZE_NEXT_EVENT);
break;
case AsyncPointer:
AllowSome(client, time, mouse, THAWED);
break;
case ReplayKeyboard:
AllowSome(client, time, keybd, NOT_GRABBED);
break;
case SyncKeyboard:
AllowSome(client, time, keybd, FREEZE_NEXT_EVENT);
break;
case AsyncKeyboard:
AllowSome(client, time, keybd, THAWED);
break;
case SyncBoth:
AllowSome(client, time, keybd, FREEZE_BOTH_NEXT_EVENT);
break;
case AsyncBoth:
AllowSome(client, time, keybd, THAWED_BOTH);
break;
default:
client->errorValue = stuff->mode;
return BadValue;
}
return Success;
}
void
ReleaseActiveGrabs(ClientPtr client)
{
DeviceIntPtr dev;
Bool done;
/* XXX CloseDownClient should remove passive grabs before
* releasing active grabs.
*/
do {
done = TRUE;
for (dev = inputInfo.devices; dev; dev = dev->next) {
if (dev->grab && SameClient(dev->grab, client)) {
(*dev->DeactivateGrab) (dev);
done = FALSE;
}
}
} while (!done);
}
/**************************************************************************
* The following procedures deal with delivering events *
**************************************************************************/
_X_EXPORT int
TryClientEvents(ClientPtr client, xEvent *pEvents, int count, Mask mask,
Mask filter, GrabPtr grab)
{
int type;
#ifdef DEBUG
if (debug_events)
ErrorF("Event([%d, %d], mask=0x%x), client=%d",
pEvents->u.u.type, pEvents->u.u.detail, mask, client->index);
#endif
if ((client) && (client != serverClient) && (!client->clientGone) &&
((filter == CantBeFiltered) || (mask & filter))) {
if (grab && !SameClient(grab, client))
return -1; /* don't send, but notify caller */
type = pEvents->u.u.type;
if (type == MotionNotify) {
if (mask & PointerMotionHintMask) {
if (WID(inputInfo.pointer->valuator->motionHintWindow) ==
pEvents->u.keyButtonPointer.event) {
#ifdef DEBUG
if (debug_events)
ErrorF("\n");
fprintf(stderr,
"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;
}
}
if (BitIsOn(criticalEvents, type)) {
#ifdef SMART_SCHEDULE
if (client->smart_priority < SMART_MAX_PRIORITY)
client->smart_priority++;
#endif
SetCriticalOutputPending();
}
WriteEventsToClient(client, count, pEvents);
#ifdef DEBUG
if (debug_events)
ErrorF(" delivered\n");
#endif
return 1;
}
else {
#ifdef DEBUG
if (debug_events)
ErrorF("\n");
#endif
return 0;
}
}
int
DeliverEventsToWindow(register WindowPtr pWin, xEvent *pEvents, int count,