altos/draw: Convert draw test framework to modern C
[fw/altos] / src / draw / frame.c
1 /*
2  * Copyright © 2023 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18 #include    <stdio.h>
19 #include    <stdlib.h>
20 #include    <ctype.h>
21 #include    <strings.h>
22 #include    <X11/Xos.h>
23 #include    <X11/Xlib.h>
24 #include    <X11/Xutil.h>
25 #include    <X11/Xatom.h>
26
27 #ifndef DEFAULT_VISUAL
28 #define DEFAULT_VISUAL 0
29 #endif
30
31 #ifndef DEFAULT_ROP
32 #define DEFAULT_ROP "copy"
33 #endif
34
35 #ifndef DEFAULT_WIDTH
36 #define DEFAULT_WIDTH 200
37 #endif
38
39 #ifndef DEFAULT_HEIGHT
40 #define DEFAULT_HEIGHT 200
41 #endif
42
43 #ifndef DEFAULT_FONT
44 #define DEFAULT_FONT 0
45 #endif
46
47 #ifndef DEFAULT_LINE_WIDTH
48 #define DEFAULT_LINE_WIDTH 0
49 #endif
50
51 #ifndef DEFAULT_LINE_STYLE
52 #define DEFAULT_LINE_STYLE NULL
53 #endif
54
55 #ifndef DEFAULT_FILL_STYLE
56 #define DEFAULT_FILL_STYLE NULL
57 #endif
58
59 #ifndef DEFAULT_CAP_STYLE
60 #define DEFAULT_CAP_STYLE NULL
61 #endif
62
63 #ifndef DEFAULT_JOIN_STYLE
64 #define DEFAULT_JOIN_STYLE NULL
65 #endif
66
67 #ifndef DEFAULT_DASHES
68 #define DEFAULT_DASHES  0
69 #define DEFAULT_NUM_DASHES 0
70 #endif
71
72 #ifdef ALL_MOTION
73 #define MOTION  PointerMotionMask
74 #else
75 #define MOTION  ButtonMotionMask
76 #endif
77
78 #ifndef EXTRA_EVENTS
79 #define EXTRA_EVENTS 0
80 #endif
81
82 unsigned long   fg_pixel;
83 unsigned long   bg_pixel;
84 int             default_rop;
85 XFontStruct     *default_font;
86
87 typedef struct _RopNames { char *name; int  rop; } RopNameRec, *RopNamePtr;
88
89 RopNameRec ropNames[] = {
90         { "clear",      GXclear,        }, /* 0 */
91         { "and",                GXand,          }, /* src AND dst */
92         { "andReverse", GXandReverse,   }, /* src AND NOT dst */
93         { "copy",               GXcopy,         }, /* src */
94         { "andInverted",        GXandInverted,  }, /* NOT src AND dst */
95         { "noop",               GXnoop,         }, /* dst */
96         { "xor",                GXxor,          }, /* src XOR dst */
97         { "or",         GXor,           }, /* src OR dst */
98         { "nor",                GXnor,          }, /* NOT src AND NOT dst */
99         { "equiv",      GXequiv,        }, /* NOT src XOR dst */
100         { "invert",     GXinvert,       }, /* NOT dst */
101         { "orReverse",  GXorReverse,    }, /* src OR NOT dst */
102         { "copyInverted",       GXcopyInverted, }, /* NOT src */
103         { "orInverted", GXorInverted,   }, /* NOT src OR dst */
104         { "nand",               GXnand,         }, /* NOT src OR NOT dst */
105         { "set",                GXset,          }, /* 1 */
106 };
107
108 static int
109 RopNameToRop (char *name)
110 {
111     int rop;
112
113     for (rop = 0; rop < 16; rop++) {
114         if (!strcmp (name, ropNames[rop].name)) {
115             return ropNames[rop].rop;
116         }
117     }
118     return GXcopy;
119 }
120
121 static void
122 Error (char *s, char *a)
123 {
124     fprintf (stderr, s, a);
125     fprintf (stderr, "\n");
126     exit (1);
127 }
128
129 static int
130 MatchCapStyle (char *s)
131 {
132     if (!strcmp (s, "round"))
133         return CapRound;
134     if (!strcmp (s, "projecting"))
135         return CapProjecting;
136     if (!strcmp (s, "butt"))
137         return CapButt;
138     if (!strcmp (s, "notlast"))
139         return CapNotLast;
140     Error ("Unknown cap style %s", s);
141     return CapNotLast;
142 }
143
144 static int
145 MatchJoinStyle (char *s)
146 {
147     if (!strcmp (s, "round"))
148         return JoinRound;
149     if (!strcmp (s, "miter"))
150         return JoinMiter;
151     if (!strcmp (s, "bevel"))
152         return JoinBevel;
153     Error ("Unknown join style %s", s);
154     return JoinBevel;
155 }
156
157 static int
158 MatchFillStyle(char *s)
159 {
160     if (!strcmp (s, "solid"))
161         return FillSolid;
162     if (!strcmp (s, "tiled"))
163         return FillTiled;
164     if (!strcmp (s, "stippled"))
165         return FillStippled;
166     if (!strcmp (s, "opaquestippled"))
167         return FillOpaqueStippled;
168     Error ("Unknown fill style %s", s);
169     return FillSolid;
170 }
171
172 static int
173 MatchLineStyle(char *s)
174 {
175     if (!strcmp (s, "solid"))
176         return LineSolid;
177     if (!strcmp (s, "onoff"))
178         return LineOnOffDash;
179     if (!strcmp (s, "double"))
180         return LineDoubleDash;
181     Error ("Unknown line style %s", s);
182     return LineSolid;
183 }
184
185 #ifdef TRACK_POINTER
186 #define PASS_BUTTONS
187 typedef struct _Position {
188     int seen;
189     int start_x, start_y;
190     int cur_x, cur_y;
191     int end_x, end_y;
192 } PositionRec, *PositionPtr;
193
194 PositionRec positions[5];
195
196 void
197 UpdatePositions (state, x, y)
198 {
199     int i;
200
201     state >>= 8;
202     for (i = 0; i < 5; i++)
203     {
204         if (state & (1 << i)) {
205             positions[i].cur_x = x;
206             positions[i].cur_y = y;
207         }
208     }
209 }
210
211 void
212 HandleButtonPress (dpy, win, gc, ev)
213     Display *dpy;
214     Window  win;
215     GC      gc;
216     XEvent  *ev;
217 {
218     XButtonEvent    *bev = (XButtonEvent *) ev;
219
220     Undraw (dpy, win, gc, positions);
221     positions[bev->button - 1].seen = 1;
222     positions[bev->button - 1].start_x = bev->x;
223     positions[bev->button - 1].start_y = bev->y;
224     positions[bev->button - 1].cur_x = bev->x;
225     positions[bev->button - 1].cur_y = bev->y;
226     positions[bev->button - 1].end_x = bev->x;
227     positions[bev->button - 1].end_y = bev->y;
228     UpdatePositions (bev->state, bev->x, bev->y);
229     Draw (dpy, win, gc, positions);
230 }
231
232 void
233 HandleButtonRelease (dpy, win, gc, ev)
234     Display *dpy;
235     Window  win;
236     GC      gc;
237     XEvent  *ev;
238 {
239     XButtonEvent    *bev = (XButtonEvent *) ev;
240
241     Undraw (dpy, win, gc, positions);
242     UpdatePositions (bev->state, bev->x, bev->y);
243     positions[bev->button - 1].end_x = bev->x;
244     positions[bev->button - 1].end_y = bev->y;
245     Draw (dpy, win, gc, positions);
246 }
247
248 void
249 HandleMotionNotify (dpy, win, gc, ev)
250     Display *dpy;
251     Window  win;
252     GC      gc;
253     XEvent  *ev;
254 {
255     XMotionEvent    *mev = (XMotionEvent *) ev;
256
257     Undraw (dpy, win, gc, positions);
258     UpdatePositions (mev->state, mev->x, mev->y);
259     Draw (dpy, win, gc, positions);
260 }
261
262 void
263 DisplayPositions (dpy, win, gc, positions)
264     Display         *dpy;
265     Window          win;
266     GC              gc;
267     PositionRec     positions[5];
268 {
269     static char text[1024];
270     static int  hastext;
271     int         pos;
272     int         i;
273     int         dir, font_ascent, font_descent;
274     XCharStruct overall;
275
276     XTextExtents (default_font , text, strlen(text),
277             &dir, &font_ascent, &font_descent, &overall);
278     if (hastext)
279         XClearArea (dpy, win, 0, 0, overall.width, font_ascent + font_descent, False);
280     pos = 0;
281     for (i = 0; i < 5; i++)
282     {
283         if (positions[i].seen)
284         {
285             if (pos)
286                 text[pos++] = ',';
287             sprintf (text + pos, "%1d: (%4d,%4d),(%4d,%4d)",
288                 i, positions[i].start_x, positions[i].start_y,
289                 positions[i].cur_x, positions[i].cur_y);
290             pos = strlen (text);
291         }
292     }
293     XDrawString (dpy, win, gc, 0, font_ascent, text, pos);
294     hastext = 1;
295 }
296
297 #endif
298
299 static inline void
300 SetToFg (Display *dpy, GC gc)
301 {
302     XSetForeground (dpy, gc, fg_pixel);
303     XSetFunction (dpy, gc, default_rop);
304 }
305
306 static inline void
307 SetToBg (Display *dpy, GC gc)
308 {
309     XSetForeground (dpy, gc, bg_pixel);
310     XSetFunction (dpy, gc, GXcopy);
311 }
312 static void
313 Usage (char *program)
314 {
315     int i;
316     fprintf (stderr, "Usage: %s\n", program);
317     fprintf (stderr, "\t-display <display-name>\n");
318     fprintf (stderr, "\t-rop {");
319     for (i = 0; i < 16; i++) {
320         fprintf (stderr, "%s", ropNames[i].name);
321         if (i == 15)
322             fprintf (stderr, "}\n");
323         else if (i % 4 == 3)
324             fprintf (stderr, ",\n\t\t");
325         else
326             fprintf (stderr, ", ");
327     }
328     fprintf (stderr, "\t-fg <fg color name>\n");
329     fprintf (stderr, "\t-bg <bg color name>\n");
330     fprintf (stderr, "\t-fg_pixel <fg pixel value>\n");
331     fprintf (stderr, "\t-bg_pixel <bg pixel value>\n");
332     fprintf (stderr, "\t-fn <font name>\n");
333     fprintf (stderr, "\t-geometry <geometry>\n");
334     fprintf (stderr, "\t-lw <line width>\n");
335     fprintf (stderr, "\t-cap {round, projecting, butt, notlast}\n");
336     fprintf (stderr, "\t-join {round, miter, bevel}\n");
337     fprintf (stderr, "\t-fill {solid, tiled, stippled, opaquestippled}\n");
338     fprintf (stderr, "\t-linestyle {solid, onoff, double}\n");
339     fprintf (stderr, "\t-bitmap <bitmap file name>\n");
340     fprintf (stderr, "\t-dashes [<dash value> ...]\n");
341     exit (1);
342 }
343
344 char    *dpy_name;
345 char    *rop_name;
346 char    *fg;
347 char    *bg;
348 char    *bitmap_file;
349 char    *cap_name = DEFAULT_CAP_STYLE;
350 char    *join_name = DEFAULT_JOIN_STYLE;
351 char    *fill_name = DEFAULT_FILL_STYLE;
352 char    *line_name = DEFAULT_LINE_STYLE;
353 char    *font_name = DEFAULT_FONT;
354 double  line_width = DEFAULT_LINE_WIDTH;
355 char    dashes[256] = { DEFAULT_DASHES };
356 int     ndashes = DEFAULT_NUM_DASHES;
357 VisualID    vid = DEFAULT_VISUAL;
358 unsigned long   planemask = ~0UL;
359 Colormap    colormap;
360 Visual      *visual;
361 int         depth;
362 Window  root = 0;
363 int         screen;
364
365 void
366 HandleExpose(Display *dpy, Window win, GC gc);
367
368 void
369 HandleKeyPress(Display *dpy, Window win, GC gc, XEvent *ev);
370
371 void
372 HandleKeyRelease(Display *dpy, Window win, GC gc, XEvent *ev);
373
374 #include <X11/extensions/Xrender.h>
375
376 XRenderColor    renderBlack = { 0, 0, 0, 0xffff };
377 XRenderColor    renderWhite = { 0xffff, 0xffff, 0xffff, 0xffff };
378 XRenderColor    renderRed = { 0xffff, 0, 0, 0xffff };
379 XRenderColor    renderGreen = { 0, 0xffff, 0, 0xffff };
380 XRenderColor    renderBlue = { 0, 0, 0xffff, 0xffff };
381
382 XRenderColor    renderClearRed = { 0x8000, 0, 0, 0x8000 };
383 XRenderColor    renderClearGreen = { 0, 0x8000, 0, 0x8000 };
384 XRenderColor    renderClearBlue = { 0, 0, 0x8000, 0x8000 };
385
386 static inline Picture
387 GetPicture (Display *dpy, Window w)
388 {
389     static Picture p;
390
391     if (!p)
392         p = XRenderCreatePicture (dpy, w, 
393                                   XRenderFindVisualFormat (dpy, visual),
394                                   0, 0);
395     return p;
396 }
397
398 static inline Picture
399 GetSrc (Display *dpy, XRenderColor *color)
400 {
401     static Picture      p;
402     static Pixmap       pix;
403     static XRenderColor lastColor;
404     XRenderPictFormat   *f;
405     XRenderPictureAttributes    attr;
406
407     if (p && !memcmp (color, &lastColor, sizeof (XRenderColor)))
408         return p;
409     if (!p)
410     {
411         f = XRenderFindStandardFormat (dpy, PictStandardARGB32);
412         pix = XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
413                              1, 1, (unsigned) f->depth);
414         attr.repeat = True;
415         p = XRenderCreatePicture (dpy, pix, f, CPRepeat, &attr);
416         XFreePixmap (dpy, pix);
417     }
418     XRenderFillRectangle (dpy, PictOpSrc, p, color, 0, 0, 1, 1);
419     lastColor = *color;
420     return p;
421 }
422
423 static inline XRenderPictFormat *
424 GetMask (Display *dpy)
425 {
426     return XRenderFindStandardFormat (dpy, PictStandardA8);
427 }
428
429 int
430 main (int argc, char **argv)
431 {
432     Display *dpy;
433     Window  win;
434     GC      gc;
435     char    **init_argv = argv;
436     XSetWindowAttributes    attr;
437     XColor  hard, exact;
438     int     x = 0, y = 0;
439     unsigned int    width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT;
440     unsigned int            border_width = 1u;
441     XSizeHints  sizeHints;
442     XWMHints    wmHints;
443     XEvent      ev;
444     XGCValues   gcv;
445 #ifdef MATCH_ARGUMENT
446     int         i;
447 #endif
448 #ifdef PASS_BUTTONS
449     int         HasMotion = 0;
450     XEvent      mev;
451 #endif
452 #ifdef COMPRESS_EXPOSE
453     XEvent      eev;
454     int         HasExpose = 0;
455 #endif
456     int         sync = 0;
457     XTextProperty   wm_name, icon_name;
458     Atom        wm_delete_window;
459     int         has_fg_pixel = 0, has_bg_pixel = 0;
460     int         has_colormap = 0;
461     unsigned long   gc_mask;
462     unsigned long   window_mask;
463
464     if (!rop_name)
465         rop_name = DEFAULT_ROP;
466     wm_name.value = (unsigned char *) argv[0];
467     wm_name.encoding = XA_STRING;
468     wm_name.format = 8;
469     wm_name.nitems = strlen ((const char *) wm_name.value) + 1;
470     icon_name = wm_name;
471     gc_mask = 0;
472     while (*++argv) {
473         if (!strcmp (*argv, "-display"))
474             dpy_name = *++argv;
475         else if (!strcmp (*argv, "-visual"))
476             vid = strtoul(*++argv, NULL, 0);
477         else if (!strcmp (*argv, "-cmap"))
478         {
479             colormap = strtoul(*++argv, NULL, 0);
480             has_colormap = 1;
481         }
482         else if (!strcmp (*argv, "-rop"))
483             rop_name = *++argv;
484         else if (!strcmp (*argv, "-fg"))
485             fg = *++argv;
486         else if (!strcmp (*argv, "-bg"))
487             bg = *++argv;
488         else if (!strcmp (*argv, "-fg_pixel"))
489         {
490             fg_pixel = strtoul(*++argv, NULL, 0);
491             has_fg_pixel = 1;
492         }
493         else if (!strcmp (*argv, "-bg_pixel"))
494         {
495             bg_pixel = strtoul(*++argv, NULL, 0);
496             has_bg_pixel = 1;
497         }
498         else if (!strcmp (*argv, "-fn"))
499             font_name = *++argv;
500         else if (!strcmp (*argv, "-pm"))
501             planemask = strtoul(*++argv, NULL, 0);
502         else if (!strcmp (*argv, "-geometry"))
503             XParseGeometry (*++argv, &x, &y, &width, &height);
504         else if (!strcmp (*argv, "-sync"))
505             sync = 1;
506         else if (!strcmp (*argv, "-bw"))
507             border_width = (unsigned int) strtoul(*++argv, NULL, 0);
508         else if (!strcmp (*argv, "-lw"))
509             line_width = strtod(*++argv, NULL);
510         else if (!strcmp (*argv, "-cap"))
511             cap_name = *++argv;
512         else if (!strcmp (*argv, "-join"))
513             join_name = *++argv;
514         else if (!strcmp (*argv, "-fill"))
515             fill_name = *++argv;
516         else if (!strcmp (*argv, "-linestyle"))
517             line_name = *++argv;
518         else if (!strcmp (*argv, "-bitmap"))
519             bitmap_file = *++argv;
520         else if (!strcmp (*argv, "-dashes"))
521         {
522             argv++;
523             ndashes = 0;
524             while (*argv && isdigit (**argv))
525                 dashes[ndashes++] = (char) atoi(*argv++);
526             argv--;
527         }
528 #ifdef MATCH_ARGUMENT
529         else if (i = MatchArgument (argv))
530             argv += i - 1;
531 #endif
532         else if (!strcmp (*argv, "-root"))
533             root = strtoul (*++argv, NULL, 0);
534         else
535             Usage (*init_argv);
536     }
537     sizeHints.flags = 0;
538     wmHints.flags = InputHint;
539     wmHints.input = True;
540     dpy = XOpenDisplay (dpy_name);
541     if (!dpy)
542         Error ("Can't open display", "");
543     if (sync)
544         XSynchronize (dpy, sync);
545     screen = DefaultScreen (dpy);
546     if (!root)
547         root = RootWindow (dpy, screen);
548     window_mask = CWBackPixel|CWBorderPixel|CWEventMask;
549     if (!has_colormap)
550         colormap = DefaultColormap (dpy, screen);
551     else
552     {
553         window_mask |= CWColormap;
554         attr.colormap = colormap;
555     }
556     visual = DefaultVisual (dpy, screen);
557     depth = DefaultDepth (dpy, screen);
558     if (vid)
559     {
560         XVisualInfo vi, *vi_ret;
561         int         n;
562
563         vi.visualid = vid;
564         vi.screen = screen;
565         vi_ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
566                                  &vi, &n);
567         if (vi_ret)
568         {
569             visual = vi_ret->visual;
570             if (!has_colormap)
571             {
572                 colormap = XCreateColormap (dpy, root, visual, AllocNone);
573                 window_mask |= CWColormap;
574                 attr.colormap = colormap;
575             }
576             depth = vi_ret->depth;
577         }
578     }
579     if (!has_fg_pixel)
580         fg_pixel = BlackPixel (dpy, screen);
581     if (!has_bg_pixel)
582         bg_pixel = WhitePixel (dpy, screen);
583     if (fg)
584     {
585         if (!XAllocNamedColor (dpy, colormap, fg, &hard, &exact))
586             Error ("Can't allocate fg pixel %s", fg);
587         fg_pixel = hard.pixel;
588     }
589     if (bg)
590     {
591         if (!XAllocNamedColor (dpy, colormap, bg, &hard, &exact))
592             Error ("Can't allocate bg pixel %s", bg);
593         bg_pixel = hard.pixel;
594     }
595     attr.background_pixel = bg_pixel;
596     attr.border_pixel = fg_pixel;
597     attr.event_mask =
598         ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|MOTION|EXTRA_EVENTS;
599 //    attr.override_redirect = True;
600 //    window_mask |= CWOverrideRedirect;
601     wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
602     win = XCreateWindow (dpy, root, x, y, width, height, border_width,
603                          depth, InputOutput,
604                          visual,
605                          window_mask,
606                          &attr);
607 #ifdef PASS_SETUP
608     Setup (dpy, win);
609 #endif
610     XSetWMProperties (dpy, win,
611                       &wm_name, &icon_name,
612                       init_argv, argc,
613                       &sizeHints, &wmHints, 0);
614     XSetWMProtocols (dpy, win, &wm_delete_window, 1);
615     default_rop =  RopNameToRop (rop_name);
616     if (line_width) 
617     {
618         gcv.line_width = (int) line_width;
619         gc_mask |= GCLineWidth;
620     }
621     if (cap_name)
622     {
623         gcv.cap_style = MatchCapStyle (cap_name);
624         gc_mask |= GCCapStyle;
625     }
626     if (fill_name) {
627         gcv.fill_style = MatchFillStyle (fill_name);
628         gc_mask |= GCFillStyle;
629     }   
630     if (line_name) {
631         gcv.line_style = MatchLineStyle (line_name);
632         gc_mask |= GCLineStyle;
633     }
634     if (join_name) {
635         gcv.join_style = MatchJoinStyle (join_name);
636         gc_mask |= GCJoinStyle;
637     }
638     if (font_name)
639         default_font = XLoadQueryFont (dpy, font_name);
640     if (default_font) {
641         gcv.font = default_font->fid;
642         gc_mask |= GCFont;
643     }
644     gcv.function = default_rop;
645     gcv.foreground = fg_pixel;
646     gcv.background = bg_pixel;
647     gcv.plane_mask = planemask;
648     gc_mask |= GCFunction|GCForeground|GCBackground|GCPlaneMask;
649     if (bitmap_file)
650     {
651         unsigned int    b_width, b_height;
652         int             ret;
653         Pixmap          bitmap, pixmap;
654
655         ret = XReadBitmapFile (dpy, win, bitmap_file, &b_width, &b_height,
656                                &bitmap, (int *) 0, (int *) 0);
657         switch (ret) {
658         case BitmapOpenFailed:
659             Error ("Can't open bitmap file %s", bitmap_file);
660             break;
661         case BitmapFileInvalid:
662             Error ("Bitmap file %s invalid", bitmap_file);
663             break;
664         case BitmapNoMemory:
665             Error ("Out of memory reading bitmap file %s", bitmap_file);
666             break;
667         case BitmapSuccess:
668             break;
669         }
670         switch (gcv.fill_style) {
671         case FillTiled:
672             pixmap = XCreatePixmap (dpy, win, b_width, b_height, (unsigned) DefaultDepth (dpy, screen));
673             gc = XCreateGC (dpy, pixmap, GCForeground|GCBackground, &gcv);
674             XCopyPlane (dpy, bitmap, pixmap, gc, 0, 0, b_width, b_height, 0, 0, 1);
675             XFreeGC (dpy, gc);
676             XFreePixmap (dpy, bitmap);
677             gcv.tile = pixmap;
678             gc_mask |= GCTile;
679             break;
680         case FillStippled:
681         case FillOpaqueStippled:
682             gcv.stipple = bitmap;
683             gc_mask |= GCStipple;
684             break;
685         }
686     }
687     gc = XCreateGC (dpy, win, gc_mask, &gcv);
688     if (!default_font)
689         default_font = XQueryFont (dpy, XGContextFromGC(gc));
690     if (ndashes)
691         XSetDashes (dpy, gc, 0, dashes, ndashes);
692     XMapWindow (dpy, win);
693     for (;;) {
694         XNextEvent (dpy, &ev);
695 #ifdef PASS_BUTTONS
696         if (HasMotion && ev.type != MotionNotify) {
697             HasMotion = 0;
698             HandleMotionNotify (dpy, win, gc, &mev);
699         }
700 #endif
701 #ifdef COMPRESS_EXPOSE
702         if (HasExpose && ev.type != Expose) {
703             HasExpose = 0;
704             HandleExpose (dpy, eev.xexpose.window, gc);
705         }
706 #endif
707         switch (ev.type) {
708         case Expose:
709 #ifdef COMPRESS_EXPOSE
710             if (QLength(dpy)) {
711                 eev = ev;
712                 HasExpose = 1;
713             } else if (ev.xexpose.count == 0)
714 #endif
715             
716             HandleExpose (dpy, ev.xexpose.window, gc);
717             break;
718 #ifdef PASS_KEYS
719         case KeyPress:
720             HandleKeyPress (dpy, ev.xkey.window, gc, &ev);
721             break;
722         case KeyRelease:
723             HandleKeyRelease (dpy, ev.xkey.window, gc, &ev);
724             break;
725 #else
726         case KeyPress:
727             if (XLookupString ((XKeyEvent *) &ev, quit_string, sizeof (quit_string), 0, 0) == 1) {
728                 switch (quit_string[0]) {
729                 case 'q':
730                     exit (0);
731                 case 'c':
732                     XClearArea (dpy, ev.xkey.window, 0, 0, 0, 0, True);
733                     break;
734                 }
735             }
736             break;
737 #endif
738 #ifdef PASS_BUTTONS
739         case ButtonPress:
740             HandleButtonPress (dpy, ev.xbutton.window, gc, &ev);
741             break;
742         case ButtonRelease:
743             HandleButtonRelease (dpy, ev.xbutton.window, gc, &ev);
744             break;
745         case MotionNotify:
746             if (QLength(dpy)) {
747                 mev = ev;
748                 HasMotion = 1;
749             } else {
750                 HandleMotionNotify (dpy, ev.xmotion.window, gc, &ev);
751             }
752             break;
753 #endif
754         case ClientMessage:
755             exit (0);
756 #ifdef PASS_OTHER
757         default:
758             HandleOtherEvent (dpy, win, gc, &ev);
759 #endif
760         }
761     }
762 }