chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / events.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "aclass.h"
26 #include "emodule.h"
27 #include "events.h"
28 #include "timers.h"
29 #include "tooltips.h"
30 #include "xwin.h"
31 #include <sys/time.h>
32 #include <X11/Xutil.h>
33 #include <X11/extensions/shape.h>
34 #if USE_XSYNC
35 #include <X11/extensions/sync.h>
36 #endif
37 #if USE_XSCREENSAVER
38 #include <X11/extensions/scrnsaver.h>
39 #endif
40 #if USE_XRANDR
41 #include <X11/extensions/Xrandr.h>
42 #endif
43 #if USE_COMPOSITE
44 #include <X11/extensions/Xcomposite.h>
45 #include <X11/extensions/Xdamage.h>
46 #include <X11/extensions/Xfixes.h>
47 #include <X11/extensions/Xrender.h>
48 #endif
49 #if USE_GLX
50 #include <GL/glx.h>
51 #endif
52
53 #if ENABLE_DEBUG_EVENTS
54 static const char  *EventName(unsigned int type);
55 #endif
56
57 /*
58  * Server extension handling
59  */
60
61 typedef struct {
62    int                 event_base, error_base;
63    int                 major, minor;
64 } EServerExtData;
65
66 typedef struct {
67    const char         *name;
68    unsigned int        ix;
69    int                 (*query_ext) (Display * dpy, int *event, int *error);
70    int                 (*query_ver) (Display * dpy, int *major, int *minor);
71    void                (*init) (int avaliable);
72 } EServerExt;
73
74 static EServerExtData ExtData[8];
75
76 #define event_base_shape ExtData[XEXT_SHAPE].event_base
77 #define event_base_randr ExtData[XEXT_RANDR].event_base
78 #define event_base_damage ExtData[XEXT_DAMAGE].event_base
79 #define event_base_saver  ExtData[XEXT_SCRSAVER].event_base
80
81 static void
82 ExtInitShape(int available)
83 {
84    if (available)
85       return;
86
87    AlertX(_("X server setup error"), _("OK"), NULL, NULL,
88           _("FATAL ERROR:\n" "\n"
89             "This Xserver does not support the Shape extension.\n"
90             "This is required for Enlightenment to run.\n" "\n"
91             "Your Xserver probably is too old or mis-configured.\n" "\n"
92             "Exiting.\n"));
93    EExit(1);
94 }
95
96 #if USE_XSYNC
97 static void
98 ExtInitSync(int available)
99 {
100    int                 i, num;
101    XSyncSystemCounter *xssc;
102
103    if (!available)
104       return;
105
106    xssc = XSyncListSystemCounters(disp, &num);
107    for (i = 0; i < num; i++)
108      {
109         if (!strcmp(xssc[i].name, "SERVERTIME"))
110            Mode.display.server_time = xssc[i].counter;
111         if (EDebug(EDBUG_TYPE_SYNC))
112            Eprintf(" Sync counter %2d: %10s %#lx %#x:%#x\n", i,
113                    xssc[i].name, xssc[i].counter,
114                    XSyncValueHigh32(xssc[i].resolution),
115                    XSyncValueLow32(xssc[i].resolution));
116      }
117    XSyncFreeSystemCounterList(xssc);
118
119    if (Mode.display.server_time == None)
120       Conf.movres.enable_sync_request = 0;
121 }
122 #endif
123
124 #if USE_XSCREENSAVER
125 static void
126 ExtInitSS(int available)
127 {
128    if (!available)
129       return;
130
131    if (EDebug(EDBUG_TYPE_VERBOSE))
132      {
133         XScreenSaverInfo   *xssi = XScreenSaverAllocInfo();
134
135         XScreenSaverQueryInfo(disp, WinGetXwin(VROOT), xssi);
136         Eprintf(" Screen saver window=%#lx\n", xssi->window);
137         XFree(xssi);
138      }
139    XScreenSaverSelectInput(disp, WinGetXwin(VROOT),
140                            ScreenSaverNotifyMask | ScreenSaverCycleMask);
141 }
142 #endif
143
144 #if USE_XRANDR
145 static void
146 ExtInitRR(int available)
147 {
148    if (!available)
149       return;
150
151    /* Listen for RandR events */
152    XRRSelectInput(disp, WinGetXwin(VROOT), RRScreenChangeNotifyMask);
153 }
154 #endif
155
156 static const EServerExt Extensions[] = {
157    {"Shape", XEXT_SHAPE, XShapeQueryExtension, XShapeQueryVersion,
158     ExtInitShape},
159 #if USE_XSYNC
160    {"Sync", XEXT_SYNC, XSyncQueryExtension, XSyncInitialize, ExtInitSync},
161 #endif
162 #if USE_XSCREENSAVER
163    {"ScrSaver", XEXT_SCRSAVER, XScreenSaverQueryExtension,
164     XScreenSaverQueryVersion, ExtInitSS},
165 #endif
166 #if USE_XRANDR
167    {"RandR", XEXT_RANDR, XRRQueryExtension, XRRQueryVersion, ExtInitRR},
168 #endif
169 #if USE_COMPOSITE
170    {"Composite", XEXT_COMPOSITE, XCompositeQueryExtension,
171     XCompositeQueryVersion, NULL},
172    {"Damage", XEXT_DAMAGE, XDamageQueryExtension, XDamageQueryVersion, NULL},
173    {"Fixes", XEXT_FIXES, XFixesQueryExtension, XFixesQueryVersion, NULL},
174    {"Render", XEXT_RENDER, XRenderQueryExtension, XRenderQueryVersion, NULL},
175 #endif
176 #if USE_GLX
177    {"GLX", XEXT_GLX, glXQueryExtension, glXQueryVersion, NULL},
178 #endif
179 };
180
181 static void
182 ExtQuery(const EServerExt * ext)
183 {
184    int                 available;
185    EServerExtData     *exd = ExtData + ext->ix;
186
187    available = ext->query_ext(disp, &(exd->event_base), &(exd->error_base));
188
189    if (available)
190      {
191         Mode.server.extensions |= 1 << ext->ix;
192
193         ext->query_ver(disp, &(exd->major), &(exd->minor));
194
195         if (EDebug(EDBUG_TYPE_VERBOSE))
196            Eprintf("Found extension %-10s version %d.%d -"
197                    " Event/error base = %d/%d\n", ext->name,
198                    exd->major, exd->minor, exd->event_base, exd->error_base);
199      }
200
201    if (ext->init)
202       ext->init(available);
203 }
204
205 /*
206  * File descriptor handling
207  */
208
209 struct _EventFdDesc {
210    const char         *name;
211    int                 fd;
212    void                (*handler) (void);
213 };
214
215 static int          nfds = 0;
216 static EventFdDesc *pfds = NULL;
217
218 EventFdDesc        *
219 EventFdRegister(int fd, EventFdHandler * handler)
220 {
221    nfds++;
222    pfds = EREALLOC(EventFdDesc, pfds, nfds);
223    pfds[nfds - 1].fd = fd;
224    pfds[nfds - 1].handler = handler;
225
226    return pfds + (nfds - 1);
227 }
228
229 void
230 EventFdUnregister(EventFdDesc * efd)
231 {
232    efd->fd = -1;
233 }
234
235 /*
236  * Event handling
237  */
238
239 #define DOUBLE_CLICK_TIME 250   /* Milliseconds */
240
241 void
242 EventsInit(void)
243 {
244    unsigned int        i;
245
246    memset(ExtData, 0, sizeof(ExtData));
247
248    for (i = 0; i < sizeof(Extensions) / sizeof(EServerExt); i++)
249       ExtQuery(Extensions + i);
250
251 #if USE_COMPOSITE
252 #define XEXT_MASK_CM_ALL ((1 << XEXT_COMPOSITE) | (1 << XEXT_DAMAGE) | \
253                           (1 << XEXT_FIXES) | (1 << XEXT_RENDER))
254    if (((Mode.server.extensions & XEXT_MASK_CM_ALL) == XEXT_MASK_CM_ALL) &&
255        (ExtData[XEXT_COMPOSITE].major > 0 ||
256         ExtData[XEXT_COMPOSITE].minor >= 2))
257       Mode.server.extensions |= 1 << XEXT_CM_ALL;
258 #endif
259
260    EventFdRegister(ConnectionNumber(disp), NULL);
261 }
262
263 int
264 EventsGetXY(int *px, int *py)
265 {
266    int                 ss;
267
268    ss = EQueryPointer(NULL, px, py, NULL, NULL);
269    Mode.events.cx = *px;
270    Mode.events.cy = *py;
271
272    return ss;
273 }
274
275 static void
276 ModeGetXY(Window rwin, int rx, int ry)
277 {
278    Window              child;
279
280    if (Mode.wm.window)
281      {
282         XTranslateCoordinates(disp, rwin, WinGetXwin(VROOT), rx, ry,
283                               &Mode.events.cx, &Mode.events.cy, &child);
284      }
285    else
286      {
287         Mode.events.cx = rx;
288         Mode.events.cy = ry;
289      }
290 }
291
292 static void
293 HandleEvent(XEvent * ev)
294 {
295    Win                 win;
296
297 #if ENABLE_DEBUG_EVENTS
298    if (EDebug(ev->type))
299       EventShow(ev);
300 #endif
301
302    win = ELookupXwin(ev->xany.window);
303
304    switch (ev->type)
305      {
306      case KeyPress:
307         Mode.events.last_keycode = ev->xkey.keycode;
308         Mode.events.last_keystate = ev->xkey.state;
309      case KeyRelease:
310         Mode.events.time = ev->xkey.time;
311         ModeGetXY(ev->xbutton.root, ev->xkey.x_root, ev->xkey.y_root);
312 #if 0                           /* FIXME - Why? */
313         if (ev->xkey.root != WinGetXwin(VROOT))
314           {
315              XSetInputFocus(disp, ev->xkey.root, RevertToPointerRoot,
316                             CurrentTime);
317              ESync();
318              ev->xkey.time = CurrentTime;
319              EXSendEvent(ev->xkey.root, 0, ev);
320              return;
321           }
322 #endif
323         Mode.events.on_screen = ev->xkey.same_screen;
324         goto do_stuff;
325
326      case ButtonPress:
327      case ButtonRelease:
328         Mode.events.time = ev->xbutton.time;
329         ModeGetXY(ev->xbutton.root, ev->xbutton.x_root, ev->xbutton.y_root);
330         Mode.events.on_screen = ev->xbutton.same_screen;
331         TooltipsHide();
332         goto do_stuff;
333
334      case MotionNotify:
335         Mode.events.time = ev->xmotion.time;
336         Mode.events.px = Mode.events.mx;
337         Mode.events.py = Mode.events.my;
338         ModeGetXY(ev->xmotion.root, ev->xmotion.x_root, ev->xmotion.y_root);
339         Mode.events.mx = Mode.events.cx;
340         Mode.events.my = Mode.events.cy;
341         Mode.events.on_screen = ev->xmotion.same_screen;
342         break;
343
344      case EnterNotify:
345         Mode.context_win = win;
346         Mode.events.time = ev->xcrossing.time;
347         Mode.events.on_screen = ev->xcrossing.same_screen;
348         if (ev->xcrossing.mode == NotifyGrab &&
349             ev->xcrossing.detail == NotifyInferior)
350           {
351              Mode.grabs.pointer_grab_window = ev->xany.window;
352              if (!Mode.grabs.pointer_grab_active)
353                 Mode.grabs.pointer_grab_active = 2;
354           }
355         ModeGetXY(ev->xcrossing.root, ev->xcrossing.x_root,
356                   ev->xcrossing.y_root);
357         TooltipsHide();
358         goto do_stuff;
359
360      case LeaveNotify:
361         Mode.events.time = ev->xcrossing.time;
362         Mode.events.on_screen = ev->xcrossing.same_screen;
363         if (ev->xcrossing.mode == NotifyGrab &&
364             ev->xcrossing.detail == NotifyInferior)
365           {
366              Mode.grabs.pointer_grab_window = None;
367              Mode.grabs.pointer_grab_active = 0;
368           }
369         ModeGetXY(ev->xcrossing.root, ev->xcrossing.x_root,
370                   ev->xcrossing.y_root);
371         TooltipsHide();
372         goto do_stuff;
373
374      case PropertyNotify:
375         Mode.events.time = ev->xproperty.time;
376         break;
377
378       do_stuff:
379         if (ev->xany.window == WinGetXwin(VROOT))
380            ActionclassesGlobalEvent(ev);
381         break;
382      }
383
384    switch (ev->type)
385      {
386      case KeyPress:             /*  2 */
387      case KeyRelease:           /*  3 */
388         /* Unfreeze keyboard in case we got here by keygrab */
389         XAllowEvents(disp, AsyncKeyboard, CurrentTime);
390         break;
391
392      case ButtonPress:          /*  4 */
393         SoundPlay(SOUND_BUTTON_CLICK);
394
395         Mode.events.double_click =
396            ((ev->xbutton.time - Mode.events.last_btime < DOUBLE_CLICK_TIME) &&
397             ev->xbutton.button == Mode.events.last_button &&
398             ev->xbutton.window == Mode.events.last_bpress2);
399
400         Mode.events.last_bpress = ev->xbutton.window;
401         Mode.events.last_bpress2 = ev->xbutton.window;
402         Mode.events.last_btime = ev->xbutton.time;
403         Mode.events.last_button = ev->xbutton.button;
404         break;
405      case ButtonRelease:        /*  5 */
406         SoundPlay(SOUND_BUTTON_RAISE);
407         break;
408      }
409
410    /* The new event dispatcher */
411    EventCallbacksProcess(win, ev);
412
413    /* Post-event stuff TBD */
414    switch (ev->type)
415      {
416      case ButtonRelease:        /*  5 */
417         Mode.events.last_bpress = 0;
418         Mode.action_inhibit = 0;
419         break;
420
421 #if 1                           /* Do this here? */
422      case DestroyNotify:
423         EUnregisterXwin(ev->xdestroywindow.window);
424         break;
425 #endif
426
427      case MappingNotify:
428         XRefreshKeyboardMapping(&ev->xmapping);
429         if (Conf.testing.bindings_reload)
430            ActionclassesReload();
431         break;
432      }
433 }
434
435 static void
436 EventsCompress(XEvent * evq, int count)
437 {
438    XEvent             *ev, *ev2;
439    int                 i, j, n;
440    int                 xa, ya, xb, yb;
441    int                 type;
442
443 #if ENABLE_DEBUG_EVENTS
444    /* Debug - should be taken out */
445    if (EDebug(EDBUG_TYPE_COMPRESSION))
446       for (i = 0; i < count; i++)
447          if (evq[i].type)
448             Eprintf("EventsCompress-1 %3d %s w=%#lx\n", i,
449                     EventName(evq[i].type), evq[i].xany.window);
450 #endif
451
452    /* Loop through event list, starting with latest */
453    for (i = count - 1; i >= 0; i--)
454      {
455         ev = evq + i;
456
457         type = ev->type;
458         switch (type)
459           {
460           case 0:
461              /* Already thrown away */
462           default:
463              break;
464
465           case MotionNotify:
466              /* Discard all but last motion event */
467              j = i - 1;
468              n = 0;
469              for (; j >= 0; j--)
470                {
471                   ev2 = evq + j;
472                   if (ev2->type == type)
473                     {
474                        n++;
475                        ev2->type = 0;
476                     }
477                }
478 #if ENABLE_DEBUG_EVENTS
479              if (n && EDebug(EDBUG_TYPE_COMPRESSION))
480                 Eprintf("EventsCompress n=%4d %s %#lx x,y = %d,%d\n",
481                         n, EventName(type), ev->xmotion.window,
482                         ev->xmotion.x, ev->xmotion.y);
483 #endif
484              break;
485
486           case LeaveNotify:
487              for (j = i - 1; j >= 0; j--)
488                {
489                   ev2 = evq + j;
490                   if (ev2->type == EnterNotify)
491                     {
492                        if (ev2->xcrossing.window == ev->xcrossing.window)
493                           goto do_enter_leave_nuked;
494                     }
495                }
496              break;
497            do_enter_leave_nuked:
498              ev2->type = ev->type = 0;
499              for (n = i - 1; n > j; n--)
500                {
501                   ev2 = evq + n;
502                   if (ev2->type == MotionNotify)
503                     {
504                        if (ev2->xmotion.window != ev->xmotion.window)
505                           continue;
506                        ev2->type = 0;
507                     }
508                }
509 #if ENABLE_DEBUG_EVENTS
510              if (EDebug(EDBUG_TYPE_COMPRESSION))
511                 Eprintf("EventsCompress n=%4d %s %#lx\n",
512                         1, EventName(type), ev->xcrossing.window);
513 #endif
514              break;
515
516           case DestroyNotify:
517              for (j = i - 1; j >= 0; j--)
518                {
519                   ev2 = evq + j;
520                   switch (ev2->type)
521                     {
522                     case CreateNotify:
523                        if (ev2->xcreatewindow.window !=
524                            ev->xdestroywindow.window)
525                           continue;
526                        ev2->type = EX_EVENT_CREATE_GONE;
527                        j = -1;  /* Break for() */
528                        break;
529                     case DestroyNotify:
530                        break;
531                     case UnmapNotify:
532                        if (ev2->xunmap.window != ev->xdestroywindow.window)
533                           continue;
534                        ev2->type = EX_EVENT_UNMAP_GONE;
535                        break;
536                     case MapNotify:
537                        if (ev2->xmap.window != ev->xdestroywindow.window)
538                           continue;
539                        ev2->type = EX_EVENT_MAP_GONE;
540                        break;
541                     case MapRequest:
542                        if (ev2->xmaprequest.window != ev->xdestroywindow.window)
543                           continue;
544                        ev2->type = EX_EVENT_MAPREQUEST_GONE;
545                        break;
546                     case ReparentNotify:
547                        if (ev2->xreparent.window != ev->xdestroywindow.window)
548                           continue;
549                        ev2->type = EX_EVENT_REPARENT_GONE;
550                        break;
551                     case ConfigureRequest:
552                        if (ev2->xconfigurerequest.window !=
553                            ev->xdestroywindow.window)
554                           continue;
555                        ev2->type = 0;
556                        break;
557                     default:
558                        /* Nuke all other events on a destroyed window */
559                        if (ev2->xany.window != ev->xdestroywindow.window)
560                           continue;
561                        ev2->type = 0;
562                        break;
563                     }
564                }
565              break;
566
567           case Expose:
568              n = 0;
569              xa = ev->xexpose.x;
570              xb = xa + ev->xexpose.width;
571              ya = ev->xexpose.y;
572              yb = ya + ev->xexpose.height;
573              for (j = i - 1; j >= 0; j--)
574                {
575                   ev2 = evq + j;
576                   if (ev2->type == type &&
577                       ev2->xexpose.window == ev->xexpose.window)
578                     {
579                        n++;
580                        ev2->type = 0;
581                        if (xa > ev2->xexpose.x)
582                           xa = ev2->xexpose.x;
583                        if (xb < ev2->xexpose.x + ev2->xexpose.width)
584                           xb = ev2->xexpose.x + ev2->xexpose.width;
585                        if (ya > ev2->xexpose.y)
586                           ya = ev2->xexpose.y;
587                        if (yb < ev2->xexpose.y + ev2->xexpose.height)
588                           yb = ev2->xexpose.y + ev2->xexpose.height;
589                     }
590                }
591              if (n)
592                {
593                   ev->xexpose.x = xa;
594                   ev->xexpose.width = xb - xa;
595                   ev->xexpose.y = ya;
596                   ev->xexpose.height = yb - ya;
597                }
598 #if ENABLE_DEBUG_EVENTS
599              if (EDebug(EDBUG_TYPE_COMPRESSION))
600                 Eprintf("EventsCompress n=%4d %s %#lx x=%4d-%4d y=%4d-%4d\n",
601                         n, EventName(type), ev->xexpose.window, xa, xb, ya, yb);
602 #endif
603              break;
604
605           case EX_EVENT_SHAPE_NOTIFY:
606              n = 0;
607              for (j = i - 1; j >= 0; j--)
608                {
609                   ev2 = evq + j;
610                   if (ev2->type == type && ev2->xany.window == ev->xany.window)
611                     {
612                        n++;
613                        ev2->type = 0;
614                     }
615                }
616 #if ENABLE_DEBUG_EVENTS
617              if (n && EDebug(EDBUG_TYPE_COMPRESSION))
618                 Eprintf("EventsCompress n=%4d %s %#lx\n",
619                         n, EventName(type), ev->xmotion.window);
620 #endif
621              break;
622
623           case GraphicsExpose:
624           case NoExpose:
625              /* Not using these */
626              ev->type = 0;
627              break;
628           }
629      }
630
631 #if ENABLE_DEBUG_EVENTS
632    /* Debug - should be taken out */
633    if (EDebug(EDBUG_TYPE_COMPRESSION))
634       for (i = 0; i < count; i++)
635          if (evq[i].type)
636             Eprintf("EventsCompress-2 %3d %s w=%#lx\n", i,
637                     EventName(evq[i].type), evq[i].xany.window);
638 #endif
639 }
640
641 static int
642 EventsFetch(XEvent ** evq_p, int *evq_n)
643 {
644    int                 i, n, count;
645    XEvent             *evq = *evq_p, *ev;
646    int                 qsz = *evq_n;
647
648    /* Fetch the entire event queue */
649    for (i = count = 0; (n = XPending(disp)) > 0;)
650      {
651         count += n;
652         if (count > qsz)
653           {
654              qsz = count;
655              evq = EREALLOC(XEvent, evq, qsz);
656           }
657         ev = evq + i;
658         for (; i < count; i++, ev++)
659           {
660              XNextEvent(disp, ev);
661
662              /* Map some event types to E internals */
663              if (ev->type == event_base_shape + ShapeNotify)
664                 ev->type = EX_EVENT_SHAPE_NOTIFY;
665 #if USE_XRANDR
666              else if (ev->type == event_base_randr + RRScreenChangeNotify)
667                 ev->type = EX_EVENT_SCREEN_CHANGE_NOTIFY;
668 #endif
669 #if USE_COMPOSITE
670              else if (ev->type == event_base_damage + XDamageNotify)
671                 ev->type = EX_EVENT_DAMAGE_NOTIFY;
672 #endif
673 #if USE_XSCREENSAVER
674              else if (ev->type == event_base_saver + ScreenSaverNotify)
675                 ev->type = EX_EVENT_SAVER_NOTIFY;
676 #endif
677           }
678      }
679
680    EventsCompress(evq, count);
681
682    *evq_p = evq;
683    *evq_n = qsz;
684
685    return count;
686 }
687
688 static int
689 EventsProcess(XEvent ** evq_p, int *evq_n, int *evq_f)
690 {
691    int                 i, n, count;
692    XEvent             *evq;
693
694    /* Fetch the entire event queue */
695    n = EventsFetch(evq_p, evq_n);
696    evq = *evq_p;
697
698    if (EDebug(EDBUG_TYPE_EVENTS))
699       Eprintf("EventsProcess-B %d\n", n);
700
701    for (i = count = 0; i < n; i++)
702      {
703         if (evq[i].type == 0)
704            continue;
705
706         if (EDebug(EDBUG_TYPE_EVENTS) > 1)
707            EventShow(evq + i);
708
709         count++;
710         HandleEvent(evq + i);
711         evq[i].type = 0;
712      }
713
714    if (EDebug(EDBUG_TYPE_EVENTS))
715       Eprintf("EventsProcess-E %d/%d\n", count, n);
716
717    if (n > *evq_f)
718       *evq_f = n;
719
720    return count;
721 }
722
723 /*
724  * This is the primary event loop.  Everything that is going to happen in the
725  * window manager has to start here at some point.  This is where all the
726  * events from the X server are interpreted, timer events are inserted, etc
727  */
728 void
729 EventsMain(void)
730 {
731    static int          evq_alloc = 0;
732    static int          evq_fetch = 0;
733    static XEvent      *evq_ptr = NULL;
734    fd_set              fdset;
735    struct timeval      tval;
736    double              time1, time2, dt;
737    int                 count, pfetch;
738    int                 fdsize, fd, i;
739
740    time1 = GetTime();
741
742    for (;;)
743      {
744         pfetch = 0;
745         count = EventsProcess(&evq_ptr, &evq_alloc, &pfetch);
746
747         if (pfetch)
748           {
749              evq_fetch =
750                 (pfetch > evq_fetch) ? pfetch : (3 * evq_fetch + pfetch) / 4;
751              if (EDebug(EDBUG_TYPE_EVENTS))
752                 Eprintf("EventsMain - Alloc/fetch/pfetch/peak=%d/%d/%d/%d)\n",
753                         evq_alloc, evq_fetch, pfetch, count);
754              if ((evq_ptr) && ((evq_alloc - evq_fetch) > 64))
755                {
756                   evq_alloc = 0;
757                   Efree(evq_ptr);
758                   evq_ptr = NULL;
759                }
760           }
761
762         /* Run idlers */
763         IdlersRun();
764
765         /* time2 = current time */
766         time2 = GetTime();
767         dt = time2 - time1;
768         time1 = time2;
769         /* dt = time spent since we last were here */
770
771         /* Run all expired timers, get time to first non-expired (0. means none) */
772         time2 = TimersRun(time2);
773
774         if (Mode.wm.exit_mode)
775            break;
776
777         if (XPending(disp))
778            continue;
779
780         FD_ZERO(&fdset);
781         fdsize = -1;
782         for (i = 0; i < nfds; i++)
783           {
784              fd = pfds[i].fd;
785              if (fd < 0)
786                 continue;
787              if (fdsize < fd)
788                 fdsize = fd;
789              FD_SET(fd, &fdset);
790           }
791         fdsize++;
792
793         if (time2 > 0.)
794           {
795              tval.tv_sec = (long)time2;
796              tval.tv_usec = (long)((time2 - ((double)tval.tv_sec)) * 1000000);
797              count = select(fdsize, &fdset, NULL, NULL, &tval);
798           }
799         else
800           {
801              count = select(fdsize, &fdset, NULL, NULL, NULL);
802           }
803
804         if (EDebug(EDBUG_TYPE_EVENTS))
805            Eprintf
806               ("EventsMain - count=%d xfd=%d:%d dt=%lf time2=%lf\n",
807                count, pfds[0].fd, FD_ISSET(pfds[0].fd, &fdset), dt, time2);
808
809         if (count == 0)
810           {
811              /* We can only get here by timeout in select */
812              TimersRun(0.);
813           }
814         else if (count > 0)
815           {
816              /* Excluding X fd */
817              for (i = 1; i < nfds; i++)
818                {
819                   fd = pfds[i].fd;
820                   if ((fd >= 0) && (FD_ISSET(fd, &fdset)))
821                     {
822                        if (EDebug(EDBUG_TYPE_EVENTS))
823                           Eprintf("Event fd %d\n", i);
824                        pfds[i].handler();
825                     }
826                }
827           }
828      }
829 }
830
831 #if ENABLE_DEBUG_EVENTS
832 /*
833  * Event debug stuff
834  */
835
836 static const char  *const TxtEventNames[] = {
837    "Error", "Reply", "KeyPress", "KeyRelease", "ButtonPress",
838    "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn",
839    "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose",
840    "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify",
841    "MapNotify",
842    "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest",
843    "GravityNotify",
844    "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
845    "SelectionClear",
846    "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage",
847    "MappingNotify"
848 };
849 #define N_EVENT_NAMES (sizeof(TxtEventNames)/sizeof(char*))
850
851 static const char  *
852 EventName(unsigned int type)
853 {
854    static char         buf[16];
855
856    if (type < N_EVENT_NAMES)
857       return TxtEventNames[type];
858
859    switch (type)
860      {
861      case EX_EVENT_CREATE_GONE:
862         return "Create-Gone";
863      case EX_EVENT_UNMAP_GONE:
864         return "Unmap-Gone";
865      case EX_EVENT_MAP_GONE:
866         return "Map-Gone";
867      case EX_EVENT_MAPREQUEST_GONE:
868         return "MapRequest-Gone";
869      case EX_EVENT_REPARENT_GONE:
870         return "Reparent-Gone";
871      case EX_EVENT_SHAPE_NOTIFY:
872         return "ShapeNotify";
873 #if USE_XSCREENSAVER
874      case EX_EVENT_SAVER_NOTIFY:
875         return "ScreenSaverNotify";
876 #endif
877 #if USE_XRANDR
878      case EX_EVENT_SCREEN_CHANGE_NOTIFY:
879         return "ScreenChangeNotify";
880 #endif
881 #if USE_COMPOSITE
882      case EX_EVENT_DAMAGE_NOTIFY:
883         return "DamageNotify";
884 #endif
885      }
886
887    sprintf(buf, "%d", type);
888    return buf;
889 }
890
891 static const char  *const TxtEventNotifyModeNames[] = {
892    "NotifyNormal", "NotifyGrab", "NotifyUngrab", "NotifyWhileGrabbed"
893 };
894 #define N_EVENT_NOTIFY_MODE_NAMES (sizeof(TxtEventNotifyModeNames)/sizeof(char*))
895
896 static const char  *
897 EventNotifyModeName(unsigned int mode)
898 {
899    if (mode < N_EVENT_NOTIFY_MODE_NAMES)
900       return TxtEventNotifyModeNames[mode];
901
902    return "Unknown";
903 }
904
905 static const char  *const TxtEventNotifyDetailNames[] = {
906    "NotifyAncestor", "NotifyVirtual", "NotifyInferior", "NotifyNonlinear",
907    "NotifyNonlinearVirtual", "NotifyPointer", "NotifyPointerRoot",
908    "NotifyDetailNone"
909 };
910 #define N_EVENT_NOTIFY_DETAIL_NAMES (sizeof(TxtEventNotifyDetailNames)/sizeof(char*))
911
912 static const char  *
913 EventNotifyDetailName(unsigned int detail)
914 {
915    if (detail < N_EVENT_NOTIFY_DETAIL_NAMES)
916       return TxtEventNotifyDetailNames[detail];
917
918    return "Unknown";
919 }
920
921 void
922 EventShow(const XEvent * ev)
923 {
924    char               *txt, buf[64];
925
926    Esnprintf(buf, sizeof(buf), "%#08lx %cEV-%s ev=%#lx",
927              ev->xany.serial, (ev->xany.send_event) ? '*' : ' ',
928              EventName(ev->type), ev->xany.window);
929
930    switch (ev->type)
931      {
932      case KeyPress:
933      case KeyRelease:
934         Eprintf("%s sub=%#lx x,y=%d,%d state=%#x keycode=%#x ss=%d\n", buf,
935                 ev->xkey.subwindow, ev->xkey.x, ev->xkey.y,
936                 ev->xkey.state, ev->xkey.keycode, ev->xkey.same_screen);
937         break;
938      case ButtonPress:
939      case ButtonRelease:
940         Eprintf("%s sub=%#lx x,y=%d,%d state=%#x button=%#x ss=%d\n", buf,
941                 ev->xbutton.subwindow, ev->xbutton.x, ev->xbutton.y,
942                 ev->xbutton.state, ev->xbutton.button, ev->xbutton.same_screen);
943         break;
944      case MotionNotify:
945         Eprintf("%s sub=%#lx x,y=%d,%d rx,ry=%d,%d ss=%d\n", buf,
946                 ev->xmotion.subwindow, ev->xmotion.x, ev->xmotion.y,
947                 ev->xmotion.x_root, ev->xmotion.y_root,
948                 ev->xmotion.same_screen);
949         break;
950      case EnterNotify:
951      case LeaveNotify:
952         Eprintf("%s sub=%#lx x,y=%d,%d m=%s d=%s ss=%d focus=%d\n", buf,
953                 ev->xcrossing.subwindow, ev->xcrossing.x, ev->xcrossing.y,
954                 EventNotifyModeName(ev->xcrossing.mode),
955                 EventNotifyDetailName(ev->xcrossing.detail),
956                 ev->xcrossing.same_screen, ev->xcrossing.focus);
957         break;
958      case FocusIn:
959      case FocusOut:
960         Eprintf("%s m=%s d=%s\n", buf, EventNotifyModeName(ev->xfocus.mode),
961                 EventNotifyDetailName(ev->xfocus.detail));
962         break;
963      case KeymapNotify:
964      case Expose:
965      case GraphicsExpose:
966         Eprintf("%sx %d+%d %dx%d\n", buf,
967                 ev->xexpose.x, ev->xexpose.y,
968                 ev->xexpose.width, ev->xexpose.height);
969         break;
970      case VisibilityNotify:
971         Eprintf("%s state=%d\n", buf, ev->xvisibility.state);
972         break;
973      case CreateNotify:
974      case DestroyNotify:
975      case UnmapNotify:
976      case MapRequest:
977      case EX_EVENT_CREATE_GONE:
978      case EX_EVENT_UNMAP_GONE:
979      case EX_EVENT_MAPREQUEST_GONE:
980         Eprintf("%s win=%#lx\n", buf, ev->xcreatewindow.window);
981         break;
982      case MapNotify:
983      case EX_EVENT_MAP_GONE:
984         Eprintf("%s win=%#lx or=%d\n", buf, ev->xmap.window,
985                 ev->xmap.override_redirect);
986         break;
987      case ReparentNotify:
988      case EX_EVENT_REPARENT_GONE:
989         Eprintf("%s win=%#lx parent=%#lx %d+%d\n", buf,
990                 ev->xreparent.window, ev->xreparent.parent,
991                 ev->xreparent.x, ev->xreparent.y);
992         break;
993      case ConfigureNotify:
994         Eprintf("%s win=%#lx %d+%d %dx%d bw=%d above=%#lx\n", buf,
995                 ev->xconfigure.window, ev->xconfigure.x,
996                 ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height,
997                 ev->xconfigure.border_width, ev->xconfigure.above);
998         break;
999      case ConfigureRequest:
1000         Eprintf("%s win=%#lx m=%#lx %d+%d %dx%d bw=%d above=%#lx stk=%d\n",
1001                 buf, ev->xconfigurerequest.window,
1002                 ev->xconfigurerequest.value_mask, ev->xconfigurerequest.x,
1003                 ev->xconfigurerequest.y, ev->xconfigurerequest.width,
1004                 ev->xconfigurerequest.height,
1005                 ev->xconfigurerequest.border_width, ev->xconfigurerequest.above,
1006                 ev->xconfigurerequest.detail);
1007         break;
1008      case GravityNotify:
1009         goto case_common;
1010      case ResizeRequest:
1011         Eprintf("%s %dx%d\n", buf,
1012                 ev->xresizerequest.width, ev->xresizerequest.height);
1013         break;
1014      case CirculateNotify:
1015      case CirculateRequest:
1016         goto case_common;
1017      case PropertyNotify:
1018         txt = XGetAtomName(disp, ev->xproperty.atom);
1019         Eprintf("%s Atom=%s(%ld)\n", buf, txt, ev->xproperty.atom);
1020         XFree(txt);
1021         break;
1022      case SelectionClear:
1023      case SelectionRequest:
1024      case SelectionNotify:
1025      case ColormapNotify:
1026         goto case_common;
1027      case ClientMessage:
1028         txt = XGetAtomName(disp, ev->xclient.message_type);
1029         Eprintf("%s ev_type=%s(%ld) data: %08lx %08lx %08lx %08lx %08lx\n",
1030                 buf, txt, ev->xclient.message_type,
1031                 ev->xclient.data.l[0], ev->xclient.data.l[1],
1032                 ev->xclient.data.l[2], ev->xclient.data.l[3],
1033                 ev->xclient.data.l[4]);
1034         XFree(txt);
1035         break;
1036      case MappingNotify:
1037         Eprintf("%s req=%d first=%d count=%d\n",
1038                 buf, ev->xmapping.request,
1039                 ev->xmapping.first_keycode, ev->xmapping.count);
1040         break;
1041
1042      case EX_EVENT_SHAPE_NOTIFY:
1043 #define se ((XShapeEvent *)ev)
1044         Eprintf("%s kind=%d shaped=%d %d,%d %dx%d\n", buf,
1045                 se->kind, se->shaped, se->x, se->y, se->width, se->height);
1046 #undef se
1047         break;
1048 #if USE_XSCREENSAVER
1049      case EX_EVENT_SAVER_NOTIFY:
1050 #define se ((XScreenSaverNotifyEvent *)ev)
1051         Eprintf("%s state=%d kind=%d\n", buf, se->state, se->kind);
1052 #undef se
1053         break;
1054 #endif
1055 #if USE_XRANDR
1056      case EX_EVENT_SCREEN_CHANGE_NOTIFY:
1057         Eprintf("%s\n", buf);
1058         break;
1059 #endif
1060 #if USE_COMPOSITE
1061 #define de ((XDamageNotifyEvent *)ev)
1062      case EX_EVENT_DAMAGE_NOTIFY:
1063         Eprintf("%s level=%d more=%x %d+%d %dx%d\n", buf,
1064                 de->level, de->more,
1065                 de->area.x, de->area.y, de->area.width, de->area.height);
1066         break;
1067 #undef de
1068 #endif
1069      default:
1070       case_common:
1071         Eprintf("%s\n", buf);
1072         break;
1073      }
1074 }
1075 #endif /* ENABLE_DEBUG_EVENTS */