1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 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 "config.h"
25 
26 #if USE_EVHAN_POLL
27 #include <poll.h>
28 #elif USE_EVHAN_SELECT
29 #include <sys/select.h>
30 #endif
31 #include <sys/time.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/extensions/shape.h>
35 #if USE_XSYNC
36 #include <X11/extensions/sync.h>
37 #endif
38 #if USE_XSCREENSAVER
39 #include <X11/extensions/scrnsaver.h>
40 #endif
41 #if USE_XRANDR
42 #include <X11/extensions/Xrandr.h>
43 #endif
44 #if USE_XINERAMA
45 #include <X11/extensions/Xinerama.h>
46 #endif
47 #if USE_COMPOSITE
48 #include <X11/extensions/Xcomposite.h>
49 #include <X11/extensions/Xdamage.h>
50 #include <X11/extensions/Xfixes.h>
51 #include <X11/extensions/Xrender.h>
52 #endif
53 #if USE_XPRESENT
54 #include <X11/extensions/Xpresent.h>
55 #define USE_GENERIC 1
56 #endif
57 #if USE_GLX
58 #include <GL/glx.h>
59 #endif
60 #if USE_XI2
61 #include <X11/extensions/XInput2.h>
62 #define USE_GENERIC 1
63 #endif
64 
65 #include "E.h"
66 #include "aclass.h"
67 #include "ecompmgr.h"
68 #include "emodule.h"
69 #include "events.h"
70 #include "timers.h"
71 #include "tooltips.h"
72 #include "xwin.h"
73 
74 #if ENABLE_DEBUG_EVENTS
75 static const char  *EventName(unsigned int type);
76 #endif
77 
78 /*
79  * Server extension handling
80  */
81 
82 typedef struct {
83    int                 version;
84    int                 major_op, event_base, error_base;
85 } EServerExtData;
86 
87 typedef struct {
88    const char         *name;
89    unsigned int        ix;
90    int                 (*query_ver)(Display * dpy, int *major, int *minor);
91    void                (*init)(int avaliable);
92 } EServerExt;
93 
94 static EServerExtData ExtData[12];
95 
96 #define event_base_shape ExtData[XEXT_SHAPE].event_base
97 #define event_base_randr ExtData[XEXT_RANDR].event_base
98 #define event_base_damage ExtData[XEXT_DAMAGE].event_base
99 #define event_base_saver  ExtData[XEXT_SCRSAVER].event_base
100 #define event_base_glx    ExtData[XEXT_GLX].event_base
101 
102 static void
ExtInitShape(int available)103 ExtInitShape(int available)
104 {
105    if (available)
106       return;
107 
108    AlertX(_("X server setup error"), _("OK"), NULL, NULL,
109 	  _("FATAL ERROR:\n" "\n"
110 	    "This Xserver does not support the Shape extension.\n"
111 	    "This is required for Enlightenment to run.\n" "\n"
112 	    "Your Xserver probably is too old or mis-configured.\n" "\n"
113 	    "Exiting.\n"));
114    EExit(1);
115 }
116 
117 #if USE_XSYNC
118 static void
ExtInitSync(int available)119 ExtInitSync(int available)
120 {
121    int                 i, num;
122    XSyncSystemCounter *xssc;
123 
124    if (!available)
125       return;
126 
127    xssc = XSyncListSystemCounters(disp, &num);
128    for (i = 0; i < num; i++)
129      {
130 	if (!strcmp(xssc[i].name, "SERVERTIME"))
131 	   Mode.display.server_time = xssc[i].counter;
132 	if (EDebug(EDBUG_TYPE_VERBOSE))
133 	   Eprintf(" Sync counter %2d: %10s %#lx %#x:%#x\n", i,
134 		   xssc[i].name, xssc[i].counter,
135 		   XSyncValueHigh32(xssc[i].resolution),
136 		   XSyncValueLow32(xssc[i].resolution));
137      }
138    XSyncFreeSystemCounterList(xssc);
139 
140    if (Mode.display.server_time == NoXID)
141       Conf.movres.enable_sync_request = 0;
142 }
143 #endif
144 
145 #if USE_XSCREENSAVER
146 static void
ExtInitSS(int available)147 ExtInitSS(int available)
148 {
149    if (!available)
150       return;
151 
152    if (EDebug(EDBUG_TYPE_VERBOSE))
153      {
154 	XScreenSaverInfo   *xssi = XScreenSaverAllocInfo();
155 
156 	XScreenSaverQueryInfo(disp, WinGetXwin(VROOT), xssi);
157 	Eprintf(" Screen saver window=%#lx\n", xssi->window);
158 	XFree(xssi);
159      }
160    XScreenSaverSelectInput(disp, WinGetXwin(VROOT),
161 			   ScreenSaverNotifyMask | ScreenSaverCycleMask);
162 }
163 #endif
164 
165 #if USE_XRANDR
166 static void
EventsRRUpdateInfo(void)167 EventsRRUpdateInfo(void)
168 {
169    XRRScreenConfiguration *sc;
170    int                 fps;
171 
172    sc = XRRGetScreenInfo(disp, WinGetXwin(VROOT));
173    fps = XRRConfigCurrentRate(sc);
174    XRRFreeScreenConfigInfo(sc);
175 
176    /* We may get e.g. fps = 0 (Xephyr) */
177    if (fps > 0 && fps < 240)
178       Mode.screen.fps = fps;
179 
180    if (EDebug(EDBUG_TYPE_VERBOSE))
181       Eprintf("Screen refresh rate = %d(%d) Hz\n", Mode.screen.fps, fps);
182 }
183 
184 static void
ExtInitRR(int available)185 ExtInitRR(int available)
186 {
187    Rotation            rot;
188 
189    if (!available)
190       return;
191 
192    /* Listen for RandR events */
193    XRRSelectInput(disp, WinGetXwin(VROOT), RRScreenChangeNotifyMask);
194 
195    XRRRotations(disp, Dpy.screen, &rot);
196    Mode.screen.rotation = rot;
197 
198    EventsRRUpdateInfo();
199 }
200 
201 void
EventsRandrScreenChange(XEvent * ev)202 EventsRandrScreenChange(XEvent * ev)
203 {
204    const XRRScreenChangeNotifyEvent *rrev = (XRRScreenChangeNotifyEvent *) ev;
205 
206    XRRUpdateConfiguration(ev);
207 
208    Mode.screen.rotation = rrev->rotation;
209 
210    EventsRRUpdateInfo();
211 }
212 #endif /* USE_XRANDR */
213 
214 #if USE_XI2
215 static              Status
EInputQueryVersion(Display * dpy,int * major_version_return,int * minor_version_return)216 EInputQueryVersion(Display * dpy,
217 		   int *major_version_return, int *minor_version_return)
218 {
219    *major_version_return = XI_2_Major;
220    *minor_version_return = XI_2_Minor;
221 
222    return XIQueryVersion(dpy, major_version_return, minor_version_return);
223 }
224 
225 #include <X11/extensions/XInput.h>
226 
227 static void
ExtInitInput(int available)228 ExtInitInput(int available)
229 {
230    int                 i, j, nd;
231    XIDeviceInfo       *dvi, *dvis;
232 
233    if (!available)
234       return;
235 
236    dvis = XIQueryDevice(disp, XIAllDevices, &nd);
237    for (i = 0; i < nd; i++)
238      {
239 	dvi = dvis + i;
240 
241 	if (dvi->use == XIMasterPointer && Mode.events.xi2_ptr == 0)
242 	   Mode.events.xi2_ptr = dvi->deviceid;
243 	else if (dvi->use == XIMasterKeyboard && Mode.events.xi2_kbd == 0)
244 	   Mode.events.xi2_kbd = dvi->deviceid;
245 
246 	if (!EDebug(EDBUG_TYPE_VERBOSE))
247 	   continue;
248 
249 	if (i == 0)
250 	   Eprintf("Dev id use att ena name\n");
251 	Eprintf(" %2d %2d %3d %3d %3d %-32s %2d", i,
252 		dvi->deviceid, dvi->use, dvi->attachment, dvi->enabled,
253 		dvi->name, dvi->num_classes);
254 	for (j = 0; j < dvi->num_classes; j++)
255 	  {
256 	     printf(" %2d/%2d", dvi->classes[j]->type,
257 		    dvi->classes[j]->sourceid);
258 	  }
259 	printf("\n");
260      }
261    XIFreeDeviceInfo(dvis);
262 }
263 #endif
264 
265 #if USE_XPRESENT
266 static void
ExtInitPresent(int available)267 ExtInitPresent(int available)
268 {
269    if (!available)
270       return;
271 
272    if (EDebug(EDBUG_TYPE_VERBOSE))
273      {
274 	Eprintf(" Capabilities: %#x\n",
275 		XPresentQueryCapabilities(disp, WinGetXwin(VROOT)));
276      }
277 }
278 #endif
279 
280 static const EServerExt Extensions[] = {
281    {"SHAPE", XEXT_SHAPE, XShapeQueryVersion, ExtInitShape},
282 #if USE_XSYNC
283    {"SYNC", XEXT_SYNC, XSyncInitialize, ExtInitSync},
284 #endif
285 #if USE_XSCREENSAVER
286    {"MIT-SCREEN-SAVER", XEXT_SCRSAVER, XScreenSaverQueryVersion, ExtInitSS},
287 #endif
288 #if USE_XRANDR
289    {"RANDR", XEXT_RANDR, XRRQueryVersion, ExtInitRR},
290 #endif
291 #if USE_XINERAMA
292    {"XINERAMA", XEXT_XINERAMA, XineramaQueryVersion, NULL},
293 #endif
294 #if USE_COMPOSITE
295    {"Composite", XEXT_COMPOSITE, XCompositeQueryVersion, NULL},
296    {"DAMAGE", XEXT_DAMAGE, XDamageQueryVersion, NULL},
297    {"XFIXES", XEXT_FIXES, XFixesQueryVersion, NULL},
298    {"RENDER", XEXT_RENDER, XRenderQueryVersion, NULL},
299 #endif
300 #if USE_GLX
301    {"GLX", XEXT_GLX, glXQueryVersion, NULL},
302 #endif
303 #if USE_XI2
304    {"XInputExtension", XEXT_XI, EInputQueryVersion, ExtInitInput},
305 #endif
306 #if USE_XPRESENT
307    {"Present", XEXT_PRESENT, XPresentQueryVersion, ExtInitPresent},
308 #endif
309 };
310 
311 static void
ExtQuery(const EServerExt * ext)312 ExtQuery(const EServerExt * ext)
313 {
314    int                 available, major, minor;
315    EServerExtData     *exd = ExtData + ext->ix;
316 
317    available = XQueryExtension(disp, ext->name, &(exd->major_op),
318 			       &(exd->event_base), &(exd->error_base));
319 
320    if (available)
321      {
322 	Mode.server.extensions |= 1 << ext->ix;
323 
324 	ext->query_ver(disp, &major, &minor);
325 	exd->version = VERS(major, minor);
326 
327 	if (EDebug(EDBUG_TYPE_VERBOSE))
328 	   Eprintf("Extension %-15s version %d.%d -"
329 		   " req/evt/err base = %3d/%3d/%3d\n", ext->name,
330 		   major, minor,
331 		   exd->major_op, exd->event_base, exd->error_base);
332      }
333 
334    if (ext->init)
335       ext->init(available);
336 }
337 
338 int
ExtVersion(int ext_ix)339 ExtVersion(int ext_ix)
340 {
341    EServerExtData     *exd = ExtData + ext_ix;
342 
343    return exd->version;
344 }
345 
346 /*
347  * File descriptor handling
348  */
349 
350 typedef struct {
351 #if 0				/* Unused */
352    const char         *name;
353 #endif
354 #if USE_EVHAN_SELECT
355    int                 fd;
356 #endif
357    void                (*handler)(void);
358 } EventFdDesc;
359 
360 static int          nfds = 0;
361 #if USE_EVHAN_POLL
362 static struct pollfd *pfdl = NULL;
363 #endif
364 static EventFdDesc *pfds = NULL;
365 
366 int
EventFdRegister(int fd,EventFdHandler * handler)367 EventFdRegister(int fd, EventFdHandler * handler)
368 {
369    int                 efd;
370 
371    efd = nfds++;
372    pfds = EREALLOC(EventFdDesc, pfds, nfds);
373 
374 #if USE_EVHAN_POLL
375    pfdl = EREALLOC(struct pollfd, pfdl, nfds);
376    pfdl[efd].fd = fd;
377 #elif USE_EVHAN_SELECT
378    pfds[efd].fd = fd;
379 #endif
380 
381    pfds[efd].handler = handler;
382 
383    return efd;
384 }
385 
386 void
EventFdUnregister(int efd)387 EventFdUnregister(int efd)
388 {
389 #if USE_EVHAN_POLL
390    if (pfdl[efd].fd > 0)
391       pfdl[efd].fd = -pfdl[efd].fd;
392 #elif USE_EVHAN_SELECT
393    pfds[efd].fd = -1;
394 #endif
395 }
396 
397 /*
398  * Event handling
399  */
400 
401 #define DOUBLE_CLICK_TIME 250	/* Milliseconds */
402 
403 void
EventsInit(void)404 EventsInit(void)
405 {
406    unsigned int        i;
407 
408    memset(ExtData, 0, sizeof(ExtData));
409 
410    Mode.screen.fps = 60;	/* If not randr or weirdness */
411 
412    for (i = 0; i < E_ARRAY_SIZE(Extensions); i++)
413       ExtQuery(Extensions + i);
414 
415 #if USE_COMPOSITE
416 #define XEXT_MASK_CM_ALL ((1 << XEXT_COMPOSITE) | (1 << XEXT_DAMAGE) | \
417                           (1 << XEXT_FIXES) | (1 << XEXT_RENDER))
418    if (((Mode.server.extensions & XEXT_MASK_CM_ALL) == XEXT_MASK_CM_ALL) &&
419        (ExtData[XEXT_COMPOSITE].version >= VERS(0, 2)))
420       Mode.server.extensions |= 1 << XEXT_CM_ALL;
421 #endif
422 
423    EventFdRegister(ConnectionNumber(disp), NULL);
424 }
425 
426 static const char  *
EventsGetExtensionName(int req)427 EventsGetExtensionName(int req)
428 {
429    unsigned int        i;
430    EServerExtData     *exd;
431 
432    for (i = 0; i < E_ARRAY_SIZE(Extensions); i++)
433      {
434 	exd = ExtData + Extensions[i].ix;
435 	if (req == exd->major_op)
436 	   return Extensions[i].name;
437      }
438 
439    return "?";
440 }
441 
442 void
EventShowError(const XEvent * evp)443 EventShowError(const XEvent * evp)
444 {
445    const XErrorEvent  *ev = &evp->xerror;
446    char                buf[64], buf1[64];
447 
448    if (ev->request_code < 128)
449       Esnprintf(buf, sizeof(buf), "%d", ev->request_code);
450    else
451       Esnprintf(buf, sizeof(buf), "%s.%d",
452 		EventsGetExtensionName(ev->request_code), ev->minor_code);
453    XGetErrorDatabaseText(disp, "XRequest", buf, "", buf1, sizeof(buf1));
454    XGetErrorText(disp, ev->error_code, buf, sizeof(buf));
455    Eprintf("*** ERROR: xid=%#lx req=%i/%i err=%i: %s: %s\n",
456 	   ev->resourceid, ev->request_code, ev->minor_code,
457 	   ev->error_code, buf1, buf);
458 }
459 
460 int
EventsUpdateXY(int * px,int * py)461 EventsUpdateXY(int *px, int *py)
462 {
463    int                 ss;
464 
465    ss = EQueryPointer(NULL, &Mode.events.cx, &Mode.events.cy, NULL, NULL);
466    if (px)
467       *px = Mode.events.cx;
468    if (py)
469       *py = Mode.events.cy;
470 
471    return ss;
472 }
473 
474 void
EventsBlock(int mode)475 EventsBlock(int mode)
476 {
477    Mode.events.block = mode;
478    if (EDebug(EDBUG_TYPE_EVENTS))
479       Eprintf("%s: mode=%d\n", __func__, Mode.events.block);
480 }
481 
482 static void
ModeGetXY(int rx,int ry)483 ModeGetXY(int rx, int ry)
484 {
485    /* Mode.wm.win_x/y should always be 0 if not in window mode */
486    Mode.events.cx = rx - Mode.wm.win_x;
487    Mode.events.cy = ry - Mode.wm.win_y;
488    if (Mode.wm.window)
489      {
490 	if (rx < Mode.wm.win_x || rx >= Mode.wm.win_x + Mode.wm.win_w ||
491 	    ry < Mode.wm.win_y || ry >= Mode.wm.win_y + Mode.wm.win_h)
492 	   Mode.events.on_screen = 0;
493      }
494 }
495 
496 static void
HandleEvent(XEvent * ev)497 HandleEvent(XEvent * ev)
498 {
499    Win                 win;
500 
501 #if ENABLE_DEBUG_EVENTS
502    if (EDebug(ev->type))
503       EventShow(ev);
504 #endif
505 
506    win = ELookupXwin(ev->xany.window);
507 
508    switch (ev->type)
509      {
510      case KeyPress:
511 	Mode.events.last_keycode = ev->xkey.keycode;
512 	Mode.events.last_keystate = ev->xkey.state;
513 	/* FALLTHROUGH */
514      case KeyRelease:
515 	Mode.events.time = ev->xkey.time;
516 	ModeGetXY(ev->xkey.x_root, ev->xkey.y_root);
517 #if 0				/* FIXME - Why? */
518 	if (ev->xkey.root != WinGetXwin(VROOT))
519 	  {
520 	     XSetInputFocus(disp, ev->xkey.root, RevertToPointerRoot,
521 			    CurrentTime);
522 	     ESync();
523 	     ev->xkey.time = CurrentTime;
524 	     EXSendEvent(ev->xkey.root, 0, ev);
525 	     return;
526 	  }
527 #endif
528 	Mode.events.on_screen = ev->xkey.same_screen;
529 	goto do_stuff;
530 
531      case ButtonPress:
532      case ButtonRelease:
533 	Mode.events.time = ev->xbutton.time;
534 	ModeGetXY(ev->xbutton.x_root, ev->xbutton.y_root);
535 	Mode.events.on_screen = ev->xbutton.same_screen;
536 	TooltipsHide();
537 	goto do_stuff;
538 
539      case MotionNotify:
540 	Mode.events.time = ev->xmotion.time;
541 	Mode.events.px = Mode.events.mx;
542 	Mode.events.py = Mode.events.my;
543 	ModeGetXY(ev->xmotion.x_root, ev->xmotion.y_root);
544 	Mode.events.mx = Mode.events.cx;
545 	Mode.events.my = Mode.events.cy;
546 	Mode.events.on_screen = ev->xmotion.same_screen;
547 	break;
548 
549      case EnterNotify:
550 	Mode.context_win = win;
551 	Mode.events.time = ev->xcrossing.time;
552 	Mode.events.on_screen = ev->xcrossing.same_screen;
553 	if (ev->xcrossing.mode == NotifyGrab &&
554 	    ev->xcrossing.detail == NotifyInferior)
555 	  {
556 	     Mode.grabs.pointer_grab_window = ev->xany.window;
557 	     if (!Mode.grabs.pointer_grab_active)
558 		Mode.grabs.pointer_grab_active = 2;
559 	  }
560 	ModeGetXY(ev->xcrossing.x_root, ev->xcrossing.y_root);
561 	TooltipsHide();
562 	goto do_stuff;
563 
564      case LeaveNotify:
565 	Mode.events.time = ev->xcrossing.time;
566 	Mode.events.on_screen = ev->xcrossing.same_screen;
567 	if (ev->xcrossing.mode == NotifyGrab &&
568 	    ev->xcrossing.detail == NotifyInferior)
569 	  {
570 	     Mode.grabs.pointer_grab_window = NoXID;
571 	     Mode.grabs.pointer_grab_active = 0;
572 	  }
573 	ModeGetXY(ev->xcrossing.x_root, ev->xcrossing.y_root);
574 	TooltipsHide();
575 	goto do_stuff;
576 
577      case PropertyNotify:
578 	Mode.events.time = ev->xproperty.time;
579 	break;
580 
581       do_stuff:
582 	if (ev->xany.window == WinGetXwin(VROOT))
583 	   ActionclassesGlobalEvent(ev);
584 	break;
585      }
586 
587    switch (ev->type)
588      {
589      case KeyPress:		/*  2 */
590      case KeyRelease:		/*  3 */
591 	/* Unfreeze keyboard in case we got here by keygrab */
592 	XAllowEvents(disp, AsyncKeyboard, CurrentTime);
593 	break;
594 
595      case ButtonPress:		/*  4 */
596 	SoundPlay(SOUND_BUTTON_CLICK);
597 
598 	Mode.events.double_click =
599 	   ((ev->xbutton.time - Mode.events.last_btime < DOUBLE_CLICK_TIME) &&
600 	    ev->xbutton.button == Mode.events.last_button &&
601 	    ev->xbutton.window == Mode.events.last_bpress2);
602 
603 	Mode.events.last_bpress = ev->xbutton.window;
604 	Mode.events.last_bpress2 = ev->xbutton.window;
605 	Mode.events.last_btime = ev->xbutton.time;
606 	Mode.events.last_button = ev->xbutton.button;
607 	break;
608      case ButtonRelease:	/*  5 */
609 	SoundPlay(SOUND_BUTTON_RAISE);
610 	break;
611      }
612 
613    /* The new event dispatcher */
614    EventCallbacksProcess(win, ev);
615 
616    /* Post-event stuff TBD */
617    switch (ev->type)
618      {
619      case ButtonRelease:	/*  5 */
620 	Mode.events.last_bpress = 0;
621 	break;
622 
623 #if 1				/* Do this here? */
624      case DestroyNotify:
625 	EUnregisterXwin(ev->xdestroywindow.window);
626 	break;
627 #endif
628 
629      case MappingNotify:
630 	XRefreshKeyboardMapping(&ev->xmapping);
631 	if (Conf.testing.bindings_reload)
632 	   ActionclassesReload();
633 	break;
634      }
635 }
636 
637 static void
EventsCompress(XEvent * evq,int count)638 EventsCompress(XEvent * evq, int count)
639 {
640    XEvent             *ev, *ev2;
641    int                 i, j, n;
642    int                 xa, ya, xb, yb;
643    int                 type;
644 
645 #if ENABLE_DEBUG_EVENTS
646    /* Debug - should be taken out */
647    if (EDebug(EDBUG_TYPE_COMPRESSION))
648       for (i = 0; i < count; i++)
649 	 if (evq[i].type)
650 	    Eprintf("%s-1 %3d %s w=%#lx\n", __func__, i,
651 		    EventName(evq[i].type), evq[i].xany.window);
652 #endif
653 
654    /* Loop through event list, starting with latest */
655    for (i = count - 1; i >= 0; i--)
656      {
657 	ev = evq + i;
658 
659 	type = ev->type;
660 	switch (type)
661 	  {
662 	  case 0:
663 	     /* Already thrown away */
664 	  default:
665 	     break;
666 
667 	  case MotionNotify:
668 	     /* Discard all but last motion event */
669 	     j = i - 1;
670 	     n = 0;
671 	     for (; j >= 0; j--)
672 	       {
673 		  ev2 = evq + j;
674 		  if (ev2->type == type)
675 		    {
676 		       n++;
677 		       ev2->type = 0;
678 		    }
679 	       }
680 #if ENABLE_DEBUG_EVENTS
681 	     if (n && EDebug(EDBUG_TYPE_COMPRESSION))
682 		Eprintf("%s: n=%4d %s %#lx x,y = %d,%d\n", __func__,
683 			n, EventName(type), ev->xmotion.window,
684 			ev->xmotion.x, ev->xmotion.y);
685 #endif
686 	     break;
687 
688 	  case LeaveNotify:
689 	     for (j = i - 1; j >= 0; j--)
690 	       {
691 		  ev2 = evq + j;
692 		  if (ev2->type == EnterNotify)
693 		    {
694 		       if (ev2->xcrossing.window == ev->xcrossing.window)
695 			  goto do_enter_leave_nuked;
696 		    }
697 	       }
698 	     break;
699 	   do_enter_leave_nuked:
700 	     ev2->type = ev->type = 0;
701 	     for (n = i - 1; n > j; n--)
702 	       {
703 		  ev2 = evq + n;
704 		  if (ev2->type == MotionNotify)
705 		    {
706 		       if (ev2->xmotion.window != ev->xmotion.window)
707 			  continue;
708 		       ev2->type = 0;
709 		    }
710 	       }
711 #if ENABLE_DEBUG_EVENTS
712 	     if (EDebug(EDBUG_TYPE_COMPRESSION))
713 		Eprintf("%s: n=%4d %s %#lx\n", __func__,
714 			1, EventName(type), ev->xcrossing.window);
715 #endif
716 	     break;
717 
718 	  case DestroyNotify:
719 	     for (j = i - 1; j >= 0; j--)
720 	       {
721 		  ev2 = evq + j;
722 		  switch (ev2->type)
723 		    {
724 		    case CreateNotify:
725 		       if (ev2->xcreatewindow.window !=
726 			   ev->xdestroywindow.window)
727 			  continue;
728 		       ev2->type = EX_EVENT_CREATE_GONE;
729 		       goto loop_quit_DestroyNotify;
730 		    case DestroyNotify:
731 		       break;
732 		    case UnmapNotify:
733 		       if (ev2->xunmap.window != ev->xdestroywindow.window)
734 			  continue;
735 		       ev2->type = EX_EVENT_UNMAP_GONE;
736 		       break;
737 		    case MapNotify:
738 		       if (ev2->xmap.window != ev->xdestroywindow.window)
739 			  continue;
740 		       ev2->type = EX_EVENT_MAP_GONE;
741 		       break;
742 		    case MapRequest:
743 		       if (ev2->xmaprequest.window != ev->xdestroywindow.window)
744 			  continue;
745 		       ev2->type = EX_EVENT_MAPREQUEST_GONE;
746 		       break;
747 		    case ReparentNotify:
748 		       if (ev2->xreparent.window != ev->xdestroywindow.window)
749 			  continue;
750 		       ev2->type = EX_EVENT_REPARENT_GONE;
751 		       break;
752 		    case ConfigureRequest:
753 		       if (ev2->xconfigurerequest.window !=
754 			   ev->xdestroywindow.window)
755 			  continue;
756 		       ev2->type = 0;
757 		       break;
758 		    default:
759 		       /* Nuke all other events on a destroyed window */
760 		       if (ev2->xany.window != ev->xdestroywindow.window)
761 			  continue;
762 		       ev2->type = 0;
763 		       break;
764 		    }
765 	       }
766 	   loop_quit_DestroyNotify:
767 	     break;
768 
769 	  case Expose:
770 	     n = 0;
771 	     xa = ev->xexpose.x;
772 	     xb = xa + ev->xexpose.width;
773 	     ya = ev->xexpose.y;
774 	     yb = ya + ev->xexpose.height;
775 	     for (j = i - 1; j >= 0; j--)
776 	       {
777 		  ev2 = evq + j;
778 		  if (ev2->type == type &&
779 		      ev2->xexpose.window == ev->xexpose.window)
780 		    {
781 		       n++;
782 		       ev2->type = 0;
783 		       if (xa > ev2->xexpose.x)
784 			  xa = ev2->xexpose.x;
785 		       if (xb < ev2->xexpose.x + ev2->xexpose.width)
786 			  xb = ev2->xexpose.x + ev2->xexpose.width;
787 		       if (ya > ev2->xexpose.y)
788 			  ya = ev2->xexpose.y;
789 		       if (yb < ev2->xexpose.y + ev2->xexpose.height)
790 			  yb = ev2->xexpose.y + ev2->xexpose.height;
791 		    }
792 	       }
793 	     if (n)
794 	       {
795 		  ev->xexpose.x = xa;
796 		  ev->xexpose.width = xb - xa;
797 		  ev->xexpose.y = ya;
798 		  ev->xexpose.height = yb - ya;
799 	       }
800 #if ENABLE_DEBUG_EVENTS
801 	     if (EDebug(EDBUG_TYPE_COMPRESSION))
802 		Eprintf("%s: n=%4d %s %#lx x=%4d-%4d y=%4d-%4d\n", __func__,
803 			n, EventName(type), ev->xexpose.window, xa, xb, ya, yb);
804 #endif
805 	     break;
806 
807 	  case EX_EVENT_SHAPE_NOTIFY:
808 	     n = 0;
809 	     for (j = i - 1; j >= 0; j--)
810 	       {
811 		  ev2 = evq + j;
812 		  if (ev2->type == type && ev2->xany.window == ev->xany.window)
813 		    {
814 		       n++;
815 		       ev2->type = 0;
816 		    }
817 	       }
818 #if ENABLE_DEBUG_EVENTS
819 	     if (n && EDebug(EDBUG_TYPE_COMPRESSION))
820 		Eprintf("%s: n=%4d %s %#lx\n", __func__,
821 			n, EventName(type), ev->xmotion.window);
822 #endif
823 	     break;
824 
825 	  case GraphicsExpose:
826 	  case NoExpose:
827 	     /* Not using these */
828 	     ev->type = 0;
829 	     break;
830 	  }
831      }
832 
833 #if ENABLE_DEBUG_EVENTS
834    /* Debug - should be taken out */
835    if (EDebug(EDBUG_TYPE_COMPRESSION))
836       for (i = 0; i < count; i++)
837 	 if (evq[i].type)
838 	    Eprintf("%s-2 %3d %s w=%#lx\n", __func__, i,
839 		    EventName(evq[i].type), evq[i].xany.window);
840 #endif
841 }
842 
843 #if USE_GENERIC
844 
845 #if USE_XI2
846 typedef union {
847    XIEvent             gen;	/* Generic XI2 */
848    XIDeviceEvent       dev;	/* Device events */
849    XIEnterEvent        elf;	/* Enter/leave, focus in/out */
850 } xie_t;
851 
852 static void
_EventFetchXI2(XEvent * ev)853 _EventFetchXI2(XEvent * ev)
854 {
855    xie_t              *xie = (xie_t *) ev->xcookie.data;
856 
857    if (EDebug(EDBUG_TYPE_XI2))
858       Eprintf("%s: %#lx: type=%d devid=%d srcid=%d\n",
859 	      __func__, xie->dev.event, xie->gen.evtype,
860 	      xie->dev.deviceid, xie->dev.sourceid);
861 
862    switch (xie->gen.evtype)
863      {
864      default:
865 	break;
866      case XI_KeyPress:
867      case XI_KeyRelease:
868      case XI_ButtonPress:
869      case XI_ButtonRelease:
870      case XI_Motion:
871 	ev->type = xie->gen.evtype;	/* Same as core */
872 #if 0
873 	/* Keep those. At least serial seems to be bad in xie. */
874 	ev->xany.serial = xie->gen.serial;
875 	ev->xany.send_event = xie->gen.send_event;
876 	ev->xany.display = xie->gen.display;
877 #endif
878 	ev->xkey.window = xie->dev.event;
879 	ev->xkey.root = xie->dev.root;
880 	ev->xkey.subwindow = xie->dev.child;
881 	ev->xkey.time = xie->gen.time;
882 	ev->xkey.x = (int)xie->dev.event_x;
883 	ev->xkey.y = (int)xie->dev.event_y;
884 	ev->xkey.x_root = (int)xie->dev.root_x;
885 	ev->xkey.y_root = (int)xie->dev.root_y;
886 	ev->xkey.state = xie->dev.mods.effective;
887 	ev->xkey.keycode = xie->dev.detail;
888 #if 0
889 	/* These are the only differences between the key/button/motion
890 	 * structs. The Xlib struct layout should ensure that things land
891 	 * appropriately (xmotion.is_hint is not used) */
892 	ev->xbutton.button = xie->dev.detail;
893 	ev->xmotion.is_hint = xie->dev.detail;	/* ??? */
894 #endif
895 	ev->xkey.same_screen = xie->dev.deviceid;	/* FIXME */
896 	break;
897      case XI_Enter:
898      case XI_Leave:
899 	ev->type = xie->gen.evtype;	/* Same as core */
900 #if 0
901 	/* Keep those. At least serial seems to be bad in xie. */
902 	ev->xany.serial = xie->gen.serial;
903 	ev->xany.send_event = xie->gen.send_event;
904 	ev->xany.display = xie->gen.display;
905 #endif
906 	ev->xcrossing.window = xie->elf.event;
907 	ev->xcrossing.root = xie->elf.root;
908 	ev->xcrossing.subwindow = xie->elf.child;
909 	ev->xcrossing.time = xie->gen.time;
910 	ev->xcrossing.x = (int)xie->elf.event_x;
911 	ev->xcrossing.y = (int)xie->elf.event_y;
912 	ev->xcrossing.x_root = (int)xie->elf.root_x;
913 	ev->xcrossing.y_root = (int)xie->elf.root_y;
914 	/* mode and detail values are the same in core/XI2.
915 	 * XI2 has a few extra modes. */
916 	ev->xcrossing.mode = xie->elf.mode;
917 	ev->xcrossing.detail = xie->elf.detail;
918 	ev->xcrossing.same_screen = xie->elf.deviceid;	/* FIXME */
919 	ev->xcrossing.focus = xie->elf.focus;
920 	ev->xcrossing.state = xie->elf.mods.effective;
921 	break;
922      case XI_FocusIn:
923      case XI_FocusOut:
924 	ev->type = xie->gen.evtype;	/* Same as core */
925 #if 0
926 	/* Keep those. At least serial seems to be bad in xie. */
927 	ev->xany.serial = xie->gen.serial;
928 	ev->xany.send_event = xie->gen.send_event;
929 	ev->xany.display = xie->gen.display;
930 #endif
931 	ev->xfocus.window = xie->elf.event;
932 	/* mode and detail values are the same in core/XI2.
933 	 * XI2 has a few extra modes. */
934 	ev->xfocus.mode = xie->elf.mode;
935 	ev->xfocus.detail = xie->elf.detail;
936 	break;
937      }
938 }
939 #endif /* USE_XI2 */
940 
941 #if USE_XPRESENT
942 typedef union {
943    XPresentEvent       xpe;
944    XPresentConfigureNotifyEvent conf;
945    XPresentCompleteNotifyEvent cmpl;
946    XPresentIdleNotifyEvent idle;
947 } xpe_t;
948 
949 static void
_EventFetchPresent(XEvent * ev)950 _EventFetchPresent(XEvent * ev)
951 {
952    xpe_t              *xpe = (xpe_t *) ev->xcookie.data;
953 
954    if (EDebug(EDBUG_TYPE_PRESENT))
955       Eprintf("%s: %#lx: type=%d\n",
956 	      __func__, xpe->idle.window, xpe->xpe.evtype);
957 }
958 #endif /* USE_XPRESENT */
959 
960 static void
_EventFetchGeneric(XEvent * ev)961 _EventFetchGeneric(XEvent * ev)
962 {
963    XGenericEventCookie gec;
964 
965    if (!XGetEventData(disp, &ev->xcookie))
966       return;
967 
968    gec = ev->xcookie;		/* Save copy for XFreeEventData() */
969 
970 #if USE_XI2
971    if (ev->xcookie.extension == ExtData[XEXT_XI].major_op)
972      {
973 	_EventFetchXI2(ev);
974 	goto done;
975      }
976 #endif
977 #if USE_XPRESENT
978    if (ev->xcookie.extension == ExtData[XEXT_PRESENT].major_op)
979      {
980 	_EventFetchPresent(ev);
981 	goto done;
982      }
983 #endif
984    /* We should never go here */
985    Eprintf("*** %s: ext=%d type=%d\n", __func__,
986 	   ev->xcookie.extension, ev->xcookie.evtype);
987 
988  done:
989    XFreeEventData(disp, &gec);
990 }
991 
992 #endif /* USE_GENERIC */
993 
994 static int
EventsFetch(XEvent ** evq_p,int * evq_n)995 EventsFetch(XEvent ** evq_p, int *evq_n)
996 {
997    int                 i, n, count;
998    XEvent             *evq = *evq_p, *ev;
999    int                 qsz = *evq_n;
1000 
1001    /* Fetch the entire event queue */
1002    for (i = count = 0; (n = XPending(disp)) > 0;)
1003      {
1004 	count += n;
1005 	if (count > qsz)
1006 	  {
1007 	     qsz = count;
1008 	     evq = EREALLOC(XEvent, evq, qsz);
1009 	  }
1010 	ev = evq + i;
1011 	for (; i < count; i++, ev++)
1012 	  {
1013 	     XNextEvent(disp, ev);
1014 #if USE_GENERIC
1015 	     if (ev->type == GenericEvent)
1016 	       {
1017 		  _EventFetchGeneric(ev);
1018 		  continue;
1019 	       }
1020 #endif
1021 
1022 	     /* Map some event types to E internals */
1023 	     if (ev->type == event_base_shape + ShapeNotify)
1024 		ev->type = EX_EVENT_SHAPE_NOTIFY;
1025 #if USE_XRANDR
1026 	     else if (ev->type == event_base_randr + RRScreenChangeNotify)
1027 		ev->type = EX_EVENT_SCREEN_CHANGE_NOTIFY;
1028 #endif
1029 #if USE_COMPOSITE
1030 	     else if (ev->type == event_base_damage + XDamageNotify)
1031 		ev->type = EX_EVENT_DAMAGE_NOTIFY;
1032 #endif
1033 #if USE_XSCREENSAVER
1034 	     else if (ev->type == event_base_saver + ScreenSaverNotify)
1035 		ev->type = EX_EVENT_SAVER_NOTIFY;
1036 #endif
1037 #if USE_GLX
1038 	     else if (ev->type == event_base_glx + GLX_BufferSwapComplete)
1039 		ev->type = EX_EVENT_GLX_FLIP;
1040 #endif
1041 	  }
1042      }
1043 
1044    EventsCompress(evq, count);
1045 
1046    *evq_p = evq;
1047    *evq_n = qsz;
1048 
1049    return count;
1050 }
1051 
1052 static int
EventsProcess(XEvent ** evq_p,int * evq_n,int * evq_f)1053 EventsProcess(XEvent ** evq_p, int *evq_n, int *evq_f)
1054 {
1055    int                 i, n, count;
1056    XEvent             *evq;
1057 
1058    /* Fetch the entire event queue */
1059    n = EventsFetch(evq_p, evq_n);
1060    evq = *evq_p;
1061 
1062    if (EDebug(EDBUG_TYPE_EVENTS) > 1)
1063       Eprintf("%s-B %d\n", __func__, n);
1064 
1065    for (i = count = 0; i < n; i++)
1066      {
1067 	if (evq[i].type == 0)
1068 	   continue;
1069 
1070 	if (EDebug(EDBUG_TYPE_EVENTS) > 2)
1071 	   EventShow(evq + i);
1072 
1073 	count++;
1074 	HandleEvent(evq + i);
1075 	evq[i].type = 0;
1076      }
1077 
1078    if (EDebug(EDBUG_TYPE_EVENTS) > 1)
1079       Eprintf("%s-E %d/%d\n", __func__, count, n);
1080 
1081    if (n > *evq_f)
1082       *evq_f = n;
1083 
1084    return count;
1085 }
1086 
1087 /*
1088  * This is the primary event loop.  Everything that is going to happen in the
1089  * window manager has to start here at some point.  This is where all the
1090  * events from the X server are interpreted, timer events are inserted, etc
1091  */
1092 void
EventsMain(void)1093 EventsMain(void)
1094 {
1095    static int          evq_alloc = 0;
1096    static int          evq_fetch = 0;
1097    static XEvent      *evq_ptr = NULL;
1098 #if USE_EVHAN_SELECT
1099    fd_set              fdset;
1100    int                 fdsize, fd;
1101    struct timeval      tval;
1102 #endif
1103    unsigned int        time1, time2;
1104    int                 dtl, dt;
1105    int                 count, pfetch;
1106    int                 i;
1107 
1108    time1 = GetTimeMs();
1109 
1110    for (;;)
1111      {
1112 	pfetch = 0;
1113 	if (!Mode.events.block)
1114 	   count = EventsProcess(&evq_ptr, &evq_alloc, &pfetch);
1115 
1116 	if (pfetch)
1117 	  {
1118 	     evq_fetch =
1119 		(pfetch > evq_fetch) ? pfetch : (3 * evq_fetch + pfetch) / 4;
1120 	     if (EDebug(EDBUG_TYPE_EVENTS) > 1)
1121 		Eprintf("%s - Alloc/fetch/pfetch/peak=%d/%d/%d/%d)\n",
1122 			__func__, evq_alloc, evq_fetch, pfetch, count);
1123 	     if ((evq_ptr) && ((evq_alloc - evq_fetch) > 64))
1124 	       {
1125 		  evq_alloc = 0;
1126 		  EFREE_NULL(evq_ptr);
1127 	       }
1128 	  }
1129 
1130 	/* time2 = current time */
1131 	time2 = GetTimeMs();
1132 	dtl = time2 - time1;
1133 	Mode.events.time_ms = time1 = time2;
1134 	Mode.events.seqn++;
1135 	/* dtl = time spent since we last were here */
1136 
1137 	/* Run all expired timers */
1138 	TimersRun(time2);
1139 
1140 	/* Run idlers */
1141 	IdlersRun();
1142 
1143 	/* Get time to first non-expired (0 means none) */
1144 	dt = TimersRunNextIn(time2);
1145 
1146 	/* Do composite rendering */
1147 	dt = ECompMgrRender(dt);
1148 
1149 	if (Mode.wm.exit_mode)
1150 	   break;
1151 
1152 	if (Mode.events.block)
1153 	   XFlush(disp);
1154 	else if (XPending(disp))
1155 	   continue;
1156 
1157 #if USE_EVHAN_POLL
1158 	for (i = 0; i < nfds; i++)
1159 	   pfdl[i].events = (i == 0 && Mode.events.block) ? 0 : POLLIN;
1160 
1161 	if (dt < 0.)
1162 	   dt = 0.;
1163 	count = poll(pfdl, nfds, (int)dt);
1164 
1165 	if (EDebug(EDBUG_TYPE_EVENTS))
1166 	   Eprintf("%s: count=%d xfd=%d:%d dtl=%.6lf dt=%.6lf\n", __func__,
1167 		   count, pfdl[0].fd, pfdl[0].revents, dtl * 1e-3, dt * 1e-3);
1168 
1169 	if (count <= 0)
1170 	   continue;		/* Timeout (or error) */
1171 
1172 	/* Excluding X fd */
1173 	for (i = 1; i < nfds; i++)
1174 	  {
1175 	     if (pfdl[i].fd >= 0 && pfdl[i].revents & POLLIN)
1176 	       {
1177 		  if (EDebug(EDBUG_TYPE_EVENTS) > 1)
1178 		     Eprintf("Event fd %d\n", i);
1179 		  pfds[i].handler();
1180 	       }
1181 	  }
1182 #elif USE_EVHAN_SELECT
1183 	FD_ZERO(&fdset);
1184 	fdsize = -1;
1185 	for (i = 0; i < nfds; i++)
1186 	  {
1187 	     if (Mode.events.block && i == 0)
1188 		continue;
1189 	     fd = pfds[i].fd;
1190 	     if (fd < 0)
1191 		continue;
1192 	     if (fdsize < fd)
1193 		fdsize = fd;
1194 	     FD_SET(fd, &fdset);
1195 	  }
1196 	fdsize++;
1197 
1198 	if (dt > 0.)
1199 	  {
1200 	     tval.tv_sec = (long)dt / 1000;
1201 	     tval.tv_usec = ((long)dt - tval.tv_sec * 1000) * 1000;
1202 	     count = select(fdsize, &fdset, NULL, NULL, &tval);
1203 	  }
1204 	else
1205 	  {
1206 	     count = select(fdsize, &fdset, NULL, NULL, NULL);
1207 	  }
1208 
1209 	if (EDebug(EDBUG_TYPE_EVENTS))
1210 	   Eprintf("%s: count=%d xfd=%d:%d dtl=%.6lf dt=%.6lf\n", __func__,
1211 		   count, pfds[0].fd, FD_ISSET(pfds[0].fd, &fdset),
1212 		   dtl * 1e-3, dt * 1e-3);
1213 
1214 	if (count <= 0)
1215 	   continue;		/* Timeout (or error) */
1216 
1217 	/* Excluding X fd */
1218 	for (i = 1; i < nfds; i++)
1219 	  {
1220 	     fd = pfds[i].fd;
1221 	     if ((fd >= 0) && (FD_ISSET(fd, &fdset)))
1222 	       {
1223 		  if (EDebug(EDBUG_TYPE_EVENTS) > 1)
1224 		     Eprintf("Event fd %d\n", i);
1225 		  pfds[i].handler();
1226 	       }
1227 	  }
1228 #endif
1229      }
1230 }
1231 
1232 #if ENABLE_DEBUG_EVENTS
1233 /*
1234  * Event debug stuff
1235  */
1236 
1237 static const char  *const TxtEventNames[] = {
1238    "Error", "Reply", "KeyPress", "KeyRelease", "ButtonPress",
1239    "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn",
1240    "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose",
1241    "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify",
1242    "MapNotify",
1243    "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest",
1244    "GravityNotify",
1245    "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
1246    "SelectionClear",
1247    "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage",
1248    "MappingNotify"
1249 };
1250 
1251 static const char  *
EventName(unsigned int type)1252 EventName(unsigned int type)
1253 {
1254    static char         buf[16];
1255 
1256    if (type < E_ARRAY_SIZE(TxtEventNames))
1257       return TxtEventNames[type];
1258 
1259    switch (type)
1260      {
1261      case EX_EVENT_CREATE_GONE:
1262 	return "Create-Gone";
1263      case EX_EVENT_UNMAP_GONE:
1264 	return "Unmap-Gone";
1265      case EX_EVENT_MAP_GONE:
1266 	return "Map-Gone";
1267      case EX_EVENT_MAPREQUEST_GONE:
1268 	return "MapRequest-Gone";
1269      case EX_EVENT_REPARENT_GONE:
1270 	return "Reparent-Gone";
1271      case EX_EVENT_SHAPE_NOTIFY:
1272 	return "ShapeNotify";
1273 #if USE_XSCREENSAVER
1274      case EX_EVENT_SAVER_NOTIFY:
1275 	return "ScreenSaverNotify";
1276 #endif
1277 #if USE_XRANDR
1278      case EX_EVENT_SCREEN_CHANGE_NOTIFY:
1279 	return "ScreenChangeNotify";
1280 #endif
1281 #if USE_COMPOSITE
1282      case EX_EVENT_DAMAGE_NOTIFY:
1283 	return "DamageNotify";
1284 #endif
1285      }
1286 
1287    sprintf(buf, "%d", type);
1288    return buf;
1289 }
1290 
1291 static const char  *const TxtEventNotifyModeNames[] = {
1292    "NotifyNormal", "NotifyGrab", "NotifyUngrab", "NotifyWhileGrabbed"
1293 };
1294 
1295 static const char  *
EventNotifyModeName(unsigned int mode)1296 EventNotifyModeName(unsigned int mode)
1297 {
1298    if (mode < E_ARRAY_SIZE(TxtEventNotifyModeNames))
1299       return TxtEventNotifyModeNames[mode];
1300 
1301    return "Unknown";
1302 }
1303 
1304 static const char  *const TxtEventNotifyDetailNames[] = {
1305    "NotifyAncestor", "NotifyVirtual", "NotifyInferior", "NotifyNonlinear",
1306    "NotifyNonlinearVirtual", "NotifyPointer", "NotifyPointerRoot",
1307    "NotifyDetailNone"
1308 };
1309 
1310 static const char  *
EventNotifyDetailName(unsigned int detail)1311 EventNotifyDetailName(unsigned int detail)
1312 {
1313    if (detail < E_ARRAY_SIZE(TxtEventNotifyDetailNames))
1314       return TxtEventNotifyDetailNames[detail];
1315 
1316    return "Unknown";
1317 }
1318 
1319 void
EventShow(const XEvent * ev)1320 EventShow(const XEvent * ev)
1321 {
1322    char               *txt, buf[64];
1323 
1324    Esnprintf(buf, sizeof(buf), "%#08lx %cEV-%s ev=%#lx",
1325 	     ev->xany.serial, (ev->xany.send_event) ? '*' : ' ',
1326 	     EventName(ev->type), ev->xany.window);
1327 
1328    switch (ev->type)
1329      {
1330      case KeyPress:
1331      case KeyRelease:
1332 	Eprintf("%s sub=%#lx x,y=%d,%d state=%#x keycode=%#x ss=%d\n", buf,
1333 		ev->xkey.subwindow, ev->xkey.x, ev->xkey.y,
1334 		ev->xkey.state, ev->xkey.keycode, ev->xkey.same_screen);
1335 	break;
1336      case ButtonPress:
1337      case ButtonRelease:
1338 	Eprintf("%s sub=%#lx x,y=%d,%d state=%#x button=%#x ss=%d\n", buf,
1339 		ev->xbutton.subwindow, ev->xbutton.x, ev->xbutton.y,
1340 		ev->xbutton.state, ev->xbutton.button, ev->xbutton.same_screen);
1341 	break;
1342      case MotionNotify:
1343 	Eprintf("%s sub=%#lx x,y=%d,%d rx,ry=%d,%d ss=%d\n", buf,
1344 		ev->xmotion.subwindow, ev->xmotion.x, ev->xmotion.y,
1345 		ev->xmotion.x_root, ev->xmotion.y_root,
1346 		ev->xmotion.same_screen);
1347 	break;
1348      case EnterNotify:
1349      case LeaveNotify:
1350 	Eprintf("%s sub=%#lx x,y=%d,%d m=%s d=%s ss=%d focus=%d\n", buf,
1351 		ev->xcrossing.subwindow, ev->xcrossing.x, ev->xcrossing.y,
1352 		EventNotifyModeName(ev->xcrossing.mode),
1353 		EventNotifyDetailName(ev->xcrossing.detail),
1354 		ev->xcrossing.same_screen, ev->xcrossing.focus);
1355 	break;
1356      case FocusIn:
1357      case FocusOut:
1358 	Eprintf("%s m=%s d=%s\n", buf, EventNotifyModeName(ev->xfocus.mode),
1359 		EventNotifyDetailName(ev->xfocus.detail));
1360 	break;
1361      case KeymapNotify:
1362      case Expose:
1363      case GraphicsExpose:
1364 	Eprintf("%sx %d+%d %dx%d\n", buf,
1365 		ev->xexpose.x, ev->xexpose.y,
1366 		ev->xexpose.width, ev->xexpose.height);
1367 	break;
1368      case VisibilityNotify:
1369 	Eprintf("%s state=%d\n", buf, ev->xvisibility.state);
1370 	break;
1371      case CreateNotify:
1372      case DestroyNotify:
1373      case UnmapNotify:
1374      case MapRequest:
1375      case EX_EVENT_CREATE_GONE:
1376      case EX_EVENT_UNMAP_GONE:
1377      case EX_EVENT_MAPREQUEST_GONE:
1378 	Eprintf("%s win=%#lx\n", buf, ev->xcreatewindow.window);
1379 	break;
1380      case MapNotify:
1381      case EX_EVENT_MAP_GONE:
1382 	Eprintf("%s win=%#lx or=%d\n", buf, ev->xmap.window,
1383 		ev->xmap.override_redirect);
1384 	break;
1385      case ReparentNotify:
1386      case EX_EVENT_REPARENT_GONE:
1387 	Eprintf("%s win=%#lx parent=%#lx %d+%d\n", buf,
1388 		ev->xreparent.window, ev->xreparent.parent,
1389 		ev->xreparent.x, ev->xreparent.y);
1390 	break;
1391      case ConfigureNotify:
1392 	Eprintf("%s win=%#lx %d+%d %dx%d bw=%d above=%#lx\n", buf,
1393 		ev->xconfigure.window, ev->xconfigure.x,
1394 		ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height,
1395 		ev->xconfigure.border_width, ev->xconfigure.above);
1396 	break;
1397      case ConfigureRequest:
1398 	Eprintf("%s win=%#lx m=%#lx %d+%d %dx%d bw=%d above=%#lx stk=%d\n",
1399 		buf, ev->xconfigurerequest.window,
1400 		ev->xconfigurerequest.value_mask, ev->xconfigurerequest.x,
1401 		ev->xconfigurerequest.y, ev->xconfigurerequest.width,
1402 		ev->xconfigurerequest.height,
1403 		ev->xconfigurerequest.border_width, ev->xconfigurerequest.above,
1404 		ev->xconfigurerequest.detail);
1405 	break;
1406      case GravityNotify:
1407 	goto case_common;
1408      case ResizeRequest:
1409 	Eprintf("%s %dx%d\n", buf,
1410 		ev->xresizerequest.width, ev->xresizerequest.height);
1411 	break;
1412      case CirculateNotify:
1413      case CirculateRequest:
1414 	goto case_common;
1415      case PropertyNotify:
1416 	txt = XGetAtomName(disp, ev->xproperty.atom);
1417 	Eprintf("%s Atom=%s(%ld)\n", buf, txt, ev->xproperty.atom);
1418 	XFree(txt);
1419 	break;
1420      case SelectionClear:
1421      case SelectionRequest:
1422      case SelectionNotify:
1423      case ColormapNotify:
1424 	goto case_common;
1425      case ClientMessage:
1426 	txt = XGetAtomName(disp, ev->xclient.message_type);
1427 	Eprintf("%s ev_type=%s(%ld) data: %08lx %08lx %08lx %08lx %08lx\n",
1428 		buf, txt, ev->xclient.message_type,
1429 		ev->xclient.data.l[0], ev->xclient.data.l[1],
1430 		ev->xclient.data.l[2], ev->xclient.data.l[3],
1431 		ev->xclient.data.l[4]);
1432 	XFree(txt);
1433 	break;
1434      case MappingNotify:
1435 	Eprintf("%s req=%d first=%d count=%d\n",
1436 		buf, ev->xmapping.request,
1437 		ev->xmapping.first_keycode, ev->xmapping.count);
1438 	break;
1439 
1440      case EX_EVENT_SHAPE_NOTIFY:
1441 #define se ((XShapeEvent *)ev)
1442 	Eprintf("%s kind=%d shaped=%d %d,%d %dx%d\n", buf,
1443 		se->kind, se->shaped, se->x, se->y, se->width, se->height);
1444 #undef se
1445 	break;
1446 #if USE_XSCREENSAVER
1447      case EX_EVENT_SAVER_NOTIFY:
1448 #define se ((XScreenSaverNotifyEvent *)ev)
1449 	Eprintf("%s state=%d kind=%d\n", buf, se->state, se->kind);
1450 #undef se
1451 	break;
1452 #endif
1453 #if USE_XRANDR
1454      case EX_EVENT_SCREEN_CHANGE_NOTIFY:
1455 	Eprintf("%s\n", buf);
1456 	break;
1457 #endif
1458 #if USE_COMPOSITE
1459 #define de ((XDamageNotifyEvent *)ev)
1460      case EX_EVENT_DAMAGE_NOTIFY:
1461 	Eprintf("%s level=%d more=%x %d+%d %dx%d\n", buf,
1462 		de->level, de->more,
1463 		de->area.x, de->area.y, de->area.width, de->area.height);
1464 	break;
1465 #undef de
1466 #endif
1467      default:
1468       case_common:
1469 	Eprintf("%s\n", buf);
1470 	break;
1471      }
1472 }
1473 #endif /* ENABLE_DEBUG_EVENTS */
1474