1/*
2 *
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Keith Packard, MIT X Consortium
26 */
27
28/*
29 * mieq.c
30 *
31 * Machine independent event queue
32 *
33 */
34
35#if HAVE_DIX_CONFIG_H
36#include <dix-config.h>
37#endif
38
39# include   <X11/X.h>
40# include   <X11/Xmd.h>
41# include   <X11/Xproto.h>
42# include   "misc.h"
43# include   "windowstr.h"
44# include   "pixmapstr.h"
45# include   "inputstr.h"
46# include   "mi.h"
47# include   "mipointer.h"
48# include   "scrnintstr.h"
49# include   <X11/extensions/XI.h>
50# include   <X11/extensions/XIproto.h>
51# include   <X11/extensions/geproto.h>
52# include   "extinit.h"
53# include   "exglobals.h"
54# include   "eventstr.h"
55
56#ifdef DPMSExtension
57# include "dpmsproc.h"
58# include <X11/extensions/dpmsconst.h>
59#endif
60
61#define QUEUE_SIZE  512
62
63#define EnqueueScreen(dev) dev->spriteInfo->sprite->pEnqueueScreen
64#define DequeueScreen(dev) dev->spriteInfo->sprite->pDequeueScreen
65
66typedef struct _Event {
67    EventListPtr    events;
68    ScreenPtr	    pScreen;
69    DeviceIntPtr    pDev; /* device this event _originated_ from */
70} EventRec, *EventPtr;
71
72typedef struct _EventQueue {
73    HWEventQueueType head, tail;         /* long for SetInputCheck */
74    CARD32           lastEventTime;      /* to avoid time running backwards */
75    int              lastMotion;         /* device ID if last event motion? */
76    EventRec         events[QUEUE_SIZE]; /* static allocation for signals */
77    mieqHandler      handlers[128];      /* custom event handler */
78} EventQueueRec, *EventQueuePtr;
79
80static EventQueueRec miEventQueue;
81
82#ifdef XQUARTZ
83#include  <pthread.h>
84static pthread_mutex_t miEventQueueMutex = PTHREAD_MUTEX_INITIALIZER;
85
86extern BOOL serverRunning;
87extern pthread_mutex_t serverRunningMutex;
88extern pthread_cond_t serverRunningCond;
89
90static inline void wait_for_server_init(void) {
91    /* If the server hasn't finished initializing, wait for it... */
92    if(!serverRunning) {
93        pthread_mutex_lock(&serverRunningMutex);
94        while(!serverRunning)
95            pthread_cond_wait(&serverRunningCond, &serverRunningMutex);
96        pthread_mutex_unlock(&serverRunningMutex);
97    }
98}
99#endif
100
101Bool
102mieqInit(void)
103{
104    int i;
105
106    miEventQueue.head = miEventQueue.tail = 0;
107    miEventQueue.lastEventTime = GetTimeInMillis ();
108    miEventQueue.lastMotion = FALSE;
109    for (i = 0; i < 128; i++)
110        miEventQueue.handlers[i] = NULL;
111    for (i = 0; i < QUEUE_SIZE; i++)
112    {
113	if (miEventQueue.events[i].events == NULL) {
114	    EventListPtr evlist = InitEventList(1);
115	    if (!evlist)
116		FatalError("Could not allocate event queue.\n");
117	    miEventQueue.events[i].events = evlist;
118	}
119    }
120
121    SetInputCheck(&miEventQueue.head, &miEventQueue.tail);
122    return TRUE;
123}
124
125void
126mieqFini(void)
127{
128    int i;
129    for (i = 0; i < QUEUE_SIZE; i++)
130    {
131	if (miEventQueue.events[i].events != NULL) {
132	    FreeEventList(miEventQueue.events[i].events, 1);
133	    miEventQueue.events[i].events = NULL;
134	}
135    }
136}
137
138/*
139 * Must be reentrant with ProcessInputEvents.  Assumption: mieqEnqueue
140 * will never be interrupted.  If this is called from both signal
141 * handlers and regular code, make sure the signal is suspended when
142 * called from regular code.
143 */
144
145void
146mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
147{
148    unsigned int           oldtail = miEventQueue.tail;
149    EventListPtr           evt;
150    int                    isMotion = 0;
151    int                    evlen;
152    Time                   time;
153
154#ifdef XQUARTZ
155    wait_for_server_init();
156    pthread_mutex_lock(&miEventQueueMutex);
157#endif
158
159    CHECKEVENT(e);
160
161    /* avoid merging events from different devices */
162    if (e->any.type == ET_Motion)
163        isMotion = pDev->id;
164
165    if (isMotion && isMotion == miEventQueue.lastMotion &&
166        oldtail != miEventQueue.head) {
167        oldtail = (oldtail - 1) % QUEUE_SIZE;
168    }
169    else {
170        static int stuck = 0;
171        /* Toss events which come in late.  Usually this means your server's
172         * stuck in an infinite loop somewhere, but SIGIO is still getting
173         * handled. */
174        if (((oldtail + 1) % QUEUE_SIZE) == miEventQueue.head) {
175            if (!stuck) {
176                ErrorF("[mi] EQ overflowing. The server is probably stuck "
177                        "in an infinite loop.\n");
178                xorg_backtrace();
179                stuck = 1;
180            }
181#ifdef XQUARTZ
182            pthread_mutex_unlock(&miEventQueueMutex);
183#endif
184	        return;
185        }
186        stuck = 0;
187    }
188
189    evlen = e->any.length;
190    evt = miEventQueue.events[oldtail].events;
191    if (evt->evlen < evlen)
192    {
193        evt->evlen = evlen;
194        evt->event = realloc(evt->event, evt->evlen);
195        if (!evt->event)
196        {
197            ErrorF("[mi] Running out of memory. Tossing event.\n");
198#ifdef XQUARTZ
199            pthread_mutex_unlock(&miEventQueueMutex);
200#endif
201            return;
202        }
203    }
204
205    memcpy(evt->event, e, evlen);
206
207    time = e->any.time;
208    /* Make sure that event times don't go backwards - this
209     * is "unnecessary", but very useful. */
210    if (time < miEventQueue.lastEventTime &&
211        miEventQueue.lastEventTime - time < 10000)
212        e->any.time = miEventQueue.lastEventTime;
213
214    miEventQueue.lastEventTime = ((InternalEvent*)evt->event)->any.time;
215    miEventQueue.events[oldtail].pScreen = pDev ? EnqueueScreen(pDev) : NULL;
216    miEventQueue.events[oldtail].pDev = pDev;
217
218    miEventQueue.lastMotion = isMotion;
219    miEventQueue.tail = (oldtail + 1) % QUEUE_SIZE;
220#ifdef XQUARTZ
221    pthread_mutex_unlock(&miEventQueueMutex);
222#endif
223}
224
225void
226mieqSwitchScreen(DeviceIntPtr pDev, ScreenPtr pScreen, Bool fromDIX)
227{
228#ifdef XQUARTZ
229    pthread_mutex_lock(&miEventQueueMutex);
230#endif
231    EnqueueScreen(pDev) = pScreen;
232    if (fromDIX)
233        DequeueScreen(pDev) = pScreen;
234#ifdef XQUARTZ
235    pthread_mutex_unlock(&miEventQueueMutex);
236#endif
237}
238
239void
240mieqSetHandler(int event, mieqHandler handler)
241{
242#ifdef XQUARTZ
243    pthread_mutex_lock(&miEventQueueMutex);
244#endif
245    if (handler && miEventQueue.handlers[event])
246        ErrorF("[mi] mieq: warning: overriding existing handler %p with %p for "
247               "event %d\n", miEventQueue.handlers[event], handler, event);
248
249    miEventQueue.handlers[event] = handler;
250#ifdef XQUARTZ
251    pthread_mutex_unlock(&miEventQueueMutex);
252#endif
253}
254
255/**
256 * Change the device id of the given event to the given device's id.
257 */
258static void
259ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
260{
261    switch(event->any.type)
262    {
263        case ET_Motion:
264        case ET_KeyPress:
265        case ET_KeyRelease:
266        case ET_ButtonPress:
267        case ET_ButtonRelease:
268        case ET_ProximityIn:
269        case ET_ProximityOut:
270        case ET_Hierarchy:
271        case ET_DeviceChanged:
272            event->device_event.deviceid = dev->id;
273            break;
274#if XFreeXDGA
275        case ET_DGAEvent:
276            break;
277#endif
278        case ET_RawKeyPress:
279        case ET_RawKeyRelease:
280        case ET_RawButtonPress:
281        case ET_RawButtonRelease:
282        case ET_RawMotion:
283            event->raw_event.deviceid = dev->id;
284            break;
285        default:
286            ErrorF("[mi] Unknown event type (%d), cannot change id.\n",
287                   event->any.type);
288    }
289}
290
291static void
292FixUpEventForMaster(DeviceIntPtr mdev, DeviceIntPtr sdev,
293                    InternalEvent* original, InternalEvent *master)
294{
295    CHECKEVENT(original);
296    CHECKEVENT(master);
297    /* Ensure chained button mappings, i.e. that the detail field is the
298     * value of the mapped button on the SD, not the physical button */
299    if (original->any.type == ET_ButtonPress ||
300        original->any.type == ET_ButtonRelease)
301    {
302        int btn = original->device_event.detail.button;
303        if (!sdev->button)
304            return; /* Should never happen */
305
306        master->device_event.detail.button = sdev->button->map[btn];
307    }
308}
309
310/**
311 * Copy the given event into master.
312 * @param sdev The slave device the original event comes from
313 * @param original The event as it came from the EQ
314 * @param copy The event after being copied
315 * @return The master device or NULL if the device is a floating slave.
316 */
317DeviceIntPtr
318CopyGetMasterEvent(DeviceIntPtr sdev,
319                   InternalEvent* original, InternalEvent *copy)
320{
321    DeviceIntPtr mdev;
322    int len = original->any.length;
323    int type = original->any.type;
324
325    CHECKEVENT(original);
326
327    /* ET_XQuartz has sdev == NULL */
328    if (!sdev || !sdev->u.master)
329        return NULL;
330
331#if XFreeXDGA
332    if (type == ET_DGAEvent)
333        type = original->dga_event.subtype;
334#endif
335
336    switch(type)
337    {
338        case ET_KeyPress:
339        case ET_KeyRelease:
340            mdev = GetMaster(sdev, MASTER_KEYBOARD);
341            break;
342        case ET_ButtonPress:
343        case ET_ButtonRelease:
344        case ET_Motion:
345        case ET_ProximityIn:
346        case ET_ProximityOut:
347            mdev = GetMaster(sdev, MASTER_POINTER);
348            break;
349        default:
350            mdev = sdev->u.master;
351            break;
352    }
353
354    memcpy(copy, original, len);
355    ChangeDeviceID(mdev, copy);
356    FixUpEventForMaster(mdev, sdev, original, copy);
357
358    return mdev;
359}
360
361
362/**
363 * Post the given @event through the device hierarchy, as appropriate.
364 * Use this function if an event must be posted for a given device during the
365 * usual event processing cycle.
366 */
367void
368mieqProcessDeviceEvent(DeviceIntPtr dev,
369                       InternalEvent *event,
370                       ScreenPtr screen)
371{
372    mieqHandler handler;
373    int x = 0, y = 0;
374    DeviceIntPtr master;
375    InternalEvent mevent; /* master event */
376
377    CHECKEVENT(event);
378
379    /* Custom event handler */
380    handler = miEventQueue.handlers[event->any.type];
381
382    switch (event->any.type) {
383        /* Catch events that include valuator information and check if they
384         * are changing the screen */
385        case ET_Motion:
386        case ET_KeyPress:
387        case ET_KeyRelease:
388        case ET_ButtonPress:
389        case ET_ButtonRelease:
390            if (dev && screen && screen != DequeueScreen(dev) && !handler) {
391                DequeueScreen(dev) = screen;
392                x = event->device_event.root_x;
393                y = event->device_event.root_y;
394                NewCurrentScreen (dev, DequeueScreen(dev), x, y);
395            }
396            break;
397        default:
398            break;
399    }
400    master = CopyGetMasterEvent(dev, event, &mevent);
401
402    if (master)
403        master->u.lastSlave = dev;
404
405    /* If someone's registered a custom event handler, let them
406     * steal it. */
407    if (handler)
408    {
409        int screenNum = dev && DequeueScreen(dev) ? DequeueScreen(dev)->myNum : (screen ? screen->myNum : 0);
410        handler(screenNum, event, dev);
411        /* Check for the SD's master in case the device got detached
412         * during event processing */
413        if (master && dev->u.master)
414            handler(screenNum, &mevent, master);
415    } else
416    {
417        /* process slave first, then master */
418        dev->public.processInputProc(event, dev);
419
420        /* Check for the SD's master in case the device got detached
421         * during event processing */
422        if (master && dev->u.master)
423            master->public.processInputProc(&mevent, master);
424    }
425}
426
427/* Call this from ProcessInputEvents(). */
428void
429mieqProcessInputEvents(void)
430{
431    EventRec *e = NULL;
432    int evlen;
433    ScreenPtr screen;
434    static InternalEvent *event = NULL;
435    static size_t event_size = 0;
436    DeviceIntPtr dev = NULL,
437                 master = NULL;
438
439#ifdef XQUARTZ
440    pthread_mutex_lock(&miEventQueueMutex);
441#endif
442
443    while (miEventQueue.head != miEventQueue.tail) {
444        e = &miEventQueue.events[miEventQueue.head];
445
446        evlen   = e->events->evlen;
447        if(evlen > event_size)
448          {
449            event = realloc(event, evlen);
450            event_size = evlen;
451          }
452
453
454        if (!event)
455            FatalError("[mi] No memory left for event processing.\n");
456
457        memcpy(event, e->events->event, evlen);
458
459
460        dev     = e->pDev;
461        screen  = e->pScreen;
462
463        miEventQueue.head = (miEventQueue.head + 1) % QUEUE_SIZE;
464
465#ifdef XQUARTZ
466        pthread_mutex_unlock(&miEventQueueMutex);
467#endif
468
469        master  = (dev && !IsMaster(dev) && dev->u.master) ? dev->u.master : NULL;
470
471        if (screenIsSaved == SCREEN_SAVER_ON)
472            dixSaveScreens (serverClient, SCREEN_SAVER_OFF, ScreenSaverReset);
473#ifdef DPMSExtension
474        else if (DPMSPowerLevel != DPMSModeOn)
475            SetScreenSaverTimer();
476
477        if (DPMSPowerLevel != DPMSModeOn)
478            DPMSSet(serverClient, DPMSModeOn);
479#endif
480
481        mieqProcessDeviceEvent(dev, event, screen);
482
483        /* Update the sprite now. Next event may be from different device. */
484        if (event->any.type == ET_Motion && master)
485            miPointerUpdateSprite(dev);
486
487#ifdef XQUARTZ
488        pthread_mutex_lock(&miEventQueueMutex);
489#endif
490    }
491#ifdef XQUARTZ
492    pthread_mutex_unlock(&miEventQueueMutex);
493#endif
494}
495
496