309 lines
11 KiB
C
309 lines
11 KiB
C
|
/* $XFree86$ */
|
||
|
/*
|
||
|
*
|
||
|
* Copyright 1990, 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.
|
||
|
*
|
||
|
* Author: Keith Packard, MIT X Consortium
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* dmxeq.c is derived from mi/mieq.c so that XInput events can be handled
|
||
|
*
|
||
|
* Modified by: Rickard E. (Rik) Faith <faith@redhat.com>
|
||
|
*
|
||
|
* Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
|
||
|
*
|
||
|
* 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 on 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
|
||
|
* NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
|
||
|
* 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
|
||
|
* This file provides an event queue that knows about XInput events.
|
||
|
* All of the code is based on mi/mieq.c and was modified as little as
|
||
|
* possible to provide XInput event support (the copyright and some of
|
||
|
* the comments are from The Open Group, Keith Packard, MIT X
|
||
|
* Consortium). (Another example of similar code is provided in
|
||
|
* hw/xfree86/common/xf86Xinput.c.) */
|
||
|
|
||
|
#ifdef HAVE_DMX_CONFIG_H
|
||
|
#include <dmx-config.h>
|
||
|
#endif
|
||
|
|
||
|
#define DMX_EQ_DEBUG 0
|
||
|
|
||
|
#include "dmx.h"
|
||
|
#include "dmxeq.h"
|
||
|
#include "dmxinput.h"
|
||
|
#include "dmxlog.h"
|
||
|
#include "dmxdpms.h"
|
||
|
|
||
|
#include "inputstr.h"
|
||
|
#include "scrnintstr.h" /* For screenInfo */
|
||
|
|
||
|
#ifdef XINPUT
|
||
|
#include <X11/extensions/XIproto.h>
|
||
|
#define EXTENSION_PROC_ARGS void *
|
||
|
#include "extinit.h" /* For LookupDeviceIntRec */
|
||
|
#endif
|
||
|
|
||
|
#if DMX_EQ_DEBUG
|
||
|
#define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b)
|
||
|
#define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e)
|
||
|
#else
|
||
|
#define DMXDBG2(f,a,b)
|
||
|
#define DMXDBG5(f,a,b,c,d,e)
|
||
|
#endif
|
||
|
|
||
|
/** The size of our queue. (The queue provided by mi/mieq.c has a size
|
||
|
* of 256.) */
|
||
|
#define QUEUE_SIZE 256
|
||
|
|
||
|
/** Information about the event. */
|
||
|
typedef struct _Event {
|
||
|
xEvent event; /**< Event. */
|
||
|
ScreenPtr pScreen; /**< Screen on which event occurred. */
|
||
|
#ifdef XINPUT
|
||
|
deviceValuator valuator; /**< XInput device valuator information. */
|
||
|
#endif
|
||
|
} EventRec, *EventPtr;
|
||
|
|
||
|
/** Event queue. */
|
||
|
typedef struct _EventQueue {
|
||
|
HWEventQueueType head; /**< Queue head; must be long for SetInputCheck. */
|
||
|
HWEventQueueType tail; /**< Queue tail; must be long for SetInputCheck. */
|
||
|
CARD32 lastEventTime; /**< To avoid time running backwards. */
|
||
|
Bool lastMotion; /**< True if last event was motion. */
|
||
|
EventRec events[QUEUE_SIZE]; /**< Static allocation for signals. */
|
||
|
DevicePtr pKbd, pPtr; /**< Device pointers (to get funcs) */
|
||
|
ScreenPtr pEnqueueScreen;/**< Screen events are delivered to. */
|
||
|
ScreenPtr pDequeueScreen;/**< Screen events are dispatched to. */
|
||
|
} EventQueueRec, *EventQueuePtr;
|
||
|
|
||
|
static EventQueueRec dmxEventQueue;
|
||
|
static Bool dmxeqInitializedFlag = FALSE;
|
||
|
|
||
|
Bool dmxeqInitialized(void)
|
||
|
{
|
||
|
return dmxeqInitializedFlag;
|
||
|
}
|
||
|
|
||
|
Bool dmxeqInit(DevicePtr pKbd, DevicePtr pPtr)
|
||
|
{
|
||
|
static unsigned long dmxGeneration = 0;
|
||
|
|
||
|
if (dmxGeneration == serverGeneration && dmxeqInitializedFlag)
|
||
|
return FALSE;
|
||
|
dmxGeneration = serverGeneration;
|
||
|
dmxeqInitializedFlag = TRUE;
|
||
|
dmxEventQueue.head = 0;
|
||
|
dmxEventQueue.tail = 0;
|
||
|
dmxEventQueue.lastEventTime = GetTimeInMillis();
|
||
|
dmxEventQueue.pKbd = pKbd;
|
||
|
dmxEventQueue.pPtr = pPtr;
|
||
|
dmxEventQueue.lastMotion = FALSE;
|
||
|
dmxEventQueue.pEnqueueScreen = screenInfo.screens[0];
|
||
|
dmxEventQueue.pDequeueScreen = dmxEventQueue.pEnqueueScreen;
|
||
|
SetInputCheck(&dmxEventQueue.head, &dmxEventQueue.tail);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function adds an event to the end of the queue. If the event is
|
||
|
* an XInput event, then the next event (the valuator event) is also
|
||
|
* stored in the queue. If the new event has a time before the time of
|
||
|
* the last event currently on the queue, then the time is updated for
|
||
|
* the new event.
|
||
|
*
|
||
|
* Must be reentrant with ProcessInputEvents. Assumption: dmxeqEnqueue
|
||
|
* will never be interrupted. If this is called from both signal
|
||
|
* handlers and regular code, make sure the signal is suspended when
|
||
|
* called from regular code.
|
||
|
*/
|
||
|
|
||
|
void dmxeqEnqueue(xEvent *e)
|
||
|
{
|
||
|
HWEventQueueType oldtail, newtail;
|
||
|
Bool isMotion;
|
||
|
|
||
|
oldtail = dmxEventQueue.tail;
|
||
|
isMotion = e->u.u.type == MotionNotify;
|
||
|
if (isMotion
|
||
|
&& dmxEventQueue.lastMotion
|
||
|
&& oldtail != dmxEventQueue.head) {
|
||
|
if (oldtail == 0) oldtail = QUEUE_SIZE;
|
||
|
oldtail = oldtail - 1;
|
||
|
} else {
|
||
|
newtail = oldtail + 1;
|
||
|
if (newtail == QUEUE_SIZE) newtail = 0;
|
||
|
/* Toss events which come in late */
|
||
|
if (newtail == dmxEventQueue.head) return;
|
||
|
dmxEventQueue.tail = newtail;
|
||
|
}
|
||
|
DMXDBG2("dmxeqEnqueue %d %d\n", dmxEventQueue.head, dmxEventQueue.tail);
|
||
|
dmxEventQueue.lastMotion = isMotion;
|
||
|
dmxEventQueue.events[oldtail].pScreen = dmxEventQueue.pEnqueueScreen;
|
||
|
|
||
|
/* Store the event in the queue */
|
||
|
dmxEventQueue.events[oldtail].event = *e;
|
||
|
#ifdef XINPUT
|
||
|
{
|
||
|
/* If this is an XInput event, store the
|
||
|
* valuator event, too */
|
||
|
deviceKeyButtonPointer *ev = (deviceKeyButtonPointer *)e;
|
||
|
if (e->u.u.type >= LASTEvent && (ev->deviceid & MORE_EVENTS))
|
||
|
dmxEventQueue.events[oldtail].valuator = *(deviceValuator *)(ev+1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Make sure that event times don't go
|
||
|
* backwards - this is "unnecessary",
|
||
|
* but very useful */
|
||
|
if (e->u.keyButtonPointer.time < dmxEventQueue.lastEventTime
|
||
|
&& dmxEventQueue.lastEventTime - e->u.keyButtonPointer.time < 10000) {
|
||
|
dmxEventQueue.events[oldtail].event.u.keyButtonPointer.time =
|
||
|
dmxEventQueue.lastEventTime;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Make \a pScreen the new screen for enqueueing events. If \a fromDIX
|
||
|
* is TRUE, also make \a pScreen the new screen for dequeuing events. */
|
||
|
void dmxeqSwitchScreen(ScreenPtr pScreen, Bool fromDIX)
|
||
|
{
|
||
|
dmxEventQueue.pEnqueueScreen = pScreen;
|
||
|
if (fromDIX) dmxEventQueue.pDequeueScreen = pScreen;
|
||
|
}
|
||
|
|
||
|
#ifdef XINPUT
|
||
|
static void dmxeqProcessXInputEvent(xEvent *xe, EventRec *e)
|
||
|
{
|
||
|
deviceKeyButtonPointer *ev = (deviceKeyButtonPointer *)xe;
|
||
|
int id = ev->deviceid & DEVICE_BITS;
|
||
|
DeviceIntPtr pDevice = LookupDeviceIntRec(id);
|
||
|
|
||
|
if (!pDevice) {
|
||
|
dmxLog(dmxError, "dmxeqProcessInputEvents: id %d not found\n", id);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!pDevice->public.processInputProc) {
|
||
|
dmxLog(dmxError,
|
||
|
"dmxeqProcessInputEvents: no processInputProc for"
|
||
|
" device id %d (%s)\n", id, pDevice->name);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ev->deviceid & MORE_EVENTS) {
|
||
|
xe[1] = *(xEvent *)(&e->valuator);
|
||
|
pDevice->public.processInputProc(xe, pDevice, 2);
|
||
|
} else {
|
||
|
pDevice->public.processInputProc(xe, pDevice, 1);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* This function is called from #ProcessInputEvents() to remove events
|
||
|
* from the queue and process them.
|
||
|
*/
|
||
|
void dmxeqProcessInputEvents(void)
|
||
|
{
|
||
|
EventRec *e;
|
||
|
int x, y;
|
||
|
xEvent xe[2];
|
||
|
|
||
|
while (dmxEventQueue.head != dmxEventQueue.tail) {
|
||
|
dmxDPMSWakeup(); /* Handles screen saver and DPMS */
|
||
|
e = &dmxEventQueue.events[dmxEventQueue.head];
|
||
|
DMXDBG5("dmxeqProcessInputEvents: type=%d screen=%p,%p root=%d,%d\n",
|
||
|
e->event.u.u.type,
|
||
|
e->pScreen, dmxEventQueue.pDequeueScreen,
|
||
|
e->event.u.keyButtonPointer.rootX,
|
||
|
e->event.u.keyButtonPointer.rootY);
|
||
|
/*
|
||
|
* Assumption - screen switching can only occur on core motion events
|
||
|
*/
|
||
|
if (e->event.u.u.type == MotionNotify
|
||
|
&& e->pScreen != dmxEventQueue.pDequeueScreen) {
|
||
|
dmxEventQueue.pDequeueScreen = e->pScreen;
|
||
|
x = e->event.u.keyButtonPointer.rootX;
|
||
|
y = e->event.u.keyButtonPointer.rootY;
|
||
|
if (dmxEventQueue.head == QUEUE_SIZE - 1) dmxEventQueue.head = 0;
|
||
|
else ++dmxEventQueue.head;
|
||
|
NewCurrentScreen(dmxEventQueue.pDequeueScreen, x, y);
|
||
|
} else {
|
||
|
xe[0] = e->event;
|
||
|
if (dmxEventQueue.head == QUEUE_SIZE - 1) dmxEventQueue.head = 0;
|
||
|
else ++dmxEventQueue.head;
|
||
|
switch (xe[0].u.u.type) {
|
||
|
case KeyPress:
|
||
|
case KeyRelease:
|
||
|
if (!dmxEventQueue.pKbd) {
|
||
|
dmxLog(dmxError, "dmxeqProcessInputEvents: No keyboard\n");
|
||
|
return;
|
||
|
}
|
||
|
dmxEventQueue.pKbd
|
||
|
->processInputProc(xe,
|
||
|
(DeviceIntPtr)dmxEventQueue.pKbd, 1);
|
||
|
break;
|
||
|
default:
|
||
|
#ifdef XINPUT
|
||
|
dmxeqProcessXInputEvent(xe, e);
|
||
|
break;
|
||
|
#endif
|
||
|
/* ifndef XINPUT, fall through */
|
||
|
case ButtonPress:
|
||
|
case ButtonRelease:
|
||
|
case MotionNotify:
|
||
|
if (!dmxEventQueue.pPtr) {
|
||
|
dmxLog(dmxError, "dmxeqProcessInputEvents: No mouse\n");
|
||
|
return;
|
||
|
}
|
||
|
dmxEventQueue.pPtr
|
||
|
->processInputProc(xe,
|
||
|
(DeviceIntPtr)dmxEventQueue.pPtr, 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|