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