altos/draw: Split out draw test scaffolding from lco-test.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    <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 void
187 HandleButtonPress(Display *dpy, Window win, GC gc, XEvent *ev);
188
189 void
190 HandleButtonRelease(Display *dpy, Window win, GC gc, XEvent *ev);
191
192 void
193 HandleMotionNotify(Display *dpy, Window win, GC gc, XEvent *ev);
194
195 #ifdef TRACK_POINTER
196
197 #define PASS_BUTTONS
198 typedef struct _Position {
199     int seen;
200     int start_x, start_y;
201     int cur_x, cur_y;
202     int end_x, end_y;
203 } PositionRec, *PositionPtr;
204
205 void
206 Draw(Display *dpy, Window win, GC gc, PositionRec positions[5]);
207
208 void
209 Undraw(Display *dpy, Window win, GC gc, PositionRec positions[5]);
210
211 PositionRec current_positions[5];
212
213 static void
214 UpdatePositions (unsigned state, int x, int y)
215 {
216     int i;
217
218     state >>= 8;
219     for (i = 0; i < 5; i++)
220     {
221         if (state & (1 << i)) {
222             current_positions[i].cur_x = x;
223             current_positions[i].cur_y = y;
224         }
225     }
226 }
227
228 void
229 HandleButtonPress (Display *dpy, Window win, GC gc, XEvent *ev)
230 {
231     XButtonEvent    *bev = (XButtonEvent *) ev;
232
233     Undraw (dpy, win, gc, current_positions);
234     current_positions[bev->button - 1].seen = 1;
235     current_positions[bev->button - 1].start_x = bev->x;
236     current_positions[bev->button - 1].start_y = bev->y;
237     current_positions[bev->button - 1].cur_x = bev->x;
238     current_positions[bev->button - 1].cur_y = bev->y;
239     current_positions[bev->button - 1].end_x = bev->x;
240     current_positions[bev->button - 1].end_y = bev->y;
241     UpdatePositions (bev->state, bev->x, bev->y);
242     Draw (dpy, win, gc, current_positions);
243 }
244
245 void
246 HandleButtonRelease (Display *dpy, Window win, GC gc, XEvent *ev)
247 {
248     XButtonEvent    *bev = (XButtonEvent *) ev;
249
250     Undraw (dpy, win, gc, current_positions);
251     UpdatePositions (bev->state, bev->x, bev->y);
252     current_positions[bev->button - 1].end_x = bev->x;
253     current_positions[bev->button - 1].end_y = bev->y;
254     Draw (dpy, win, gc, current_positions);
255 }
256
257 void
258 HandleMotionNotify (Display *dpy, Window win, GC gc, XEvent *ev)
259 {
260     XMotionEvent    *mev = (XMotionEvent *) ev;
261
262     Undraw (dpy, win, gc, current_positions);
263     UpdatePositions (mev->state, mev->x, mev->y);
264     Draw (dpy, win, gc, current_positions);
265 }
266
267 static inline void
268 DisplayPositions (Display *dpy, Window win, GC gc, 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, (int) strlen(text),
278             &dir, &font_ascent, &font_descent, &overall);
279     if (hastext)
280             XClearArea (dpy, win, 0, 0, (unsigned) overall.width, (unsigned) (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 = (int) 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 Setup(Display *dpy, Window win);
371
372 void
373 HandleExpose(Display *dpy, Window win, GC gc);
374
375 void
376 HandleKeyPress(Display *dpy, Window win, GC gc, XEvent *ev);
377
378 void
379 HandleKeyRelease(Display *dpy, Window win, GC gc, XEvent *ev);
380
381 void
382 HandleTimeout(Display *dpy, Window win, GC gc);
383
384 #include <X11/extensions/Xrender.h>
385
386 XRenderColor    renderBlack = { 0, 0, 0, 0xffff };
387 XRenderColor    renderWhite = { 0xffff, 0xffff, 0xffff, 0xffff };
388 XRenderColor    renderRed = { 0xffff, 0, 0, 0xffff };
389 XRenderColor    renderGreen = { 0, 0xffff, 0, 0xffff };
390 XRenderColor    renderBlue = { 0, 0, 0xffff, 0xffff };
391
392 XRenderColor    renderClearRed = { 0x8000, 0, 0, 0x8000 };
393 XRenderColor    renderClearGreen = { 0, 0x8000, 0, 0x8000 };
394 XRenderColor    renderClearBlue = { 0, 0, 0x8000, 0x8000 };
395
396 static inline Picture
397 GetPicture (Display *dpy, Window w)
398 {
399     static Picture p;
400
401     if (!p)
402         p = XRenderCreatePicture (dpy, w, 
403                                   XRenderFindVisualFormat (dpy, visual),
404                                   0, 0);
405     return p;
406 }
407
408 static inline Picture
409 GetSrc (Display *dpy, XRenderColor *color)
410 {
411     static Picture      p;
412     static Pixmap       pix;
413     static XRenderColor lastColor;
414     XRenderPictFormat   *f;
415     XRenderPictureAttributes    attr;
416
417     if (p && !memcmp (color, &lastColor, sizeof (XRenderColor)))
418         return p;
419     if (!p)
420     {
421         f = XRenderFindStandardFormat (dpy, PictStandardARGB32);
422         pix = XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
423                              1, 1, (unsigned) f->depth);
424         attr.repeat = True;
425         p = XRenderCreatePicture (dpy, pix, f, CPRepeat, &attr);
426         XFreePixmap (dpy, pix);
427     }
428     XRenderFillRectangle (dpy, PictOpSrc, p, color, 0, 0, 1, 1);
429     lastColor = *color;
430     return p;
431 }
432
433 static inline XRenderPictFormat *
434 GetMask (Display *dpy)
435 {
436     return XRenderFindStandardFormat (dpy, PictStandardA8);
437 }
438
439 int
440 main (int argc, char **argv)
441 {
442     Display *dpy;
443     Window  win;
444     GC      gc;
445     char    **init_argv = argv;
446     XSetWindowAttributes    attr;
447     XColor  hard, exact;
448     int     x = 0, y = 0;
449     unsigned int    width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT;
450     unsigned int            border_width = 1u;
451     XSizeHints  sizeHints;
452     XWMHints    wmHints;
453     XEvent      ev;
454     XGCValues   gcv;
455 #ifdef MATCH_ARGUMENT
456     int         i;
457 #endif
458 #ifdef PASS_BUTTONS
459     int         HasMotion = 0;
460     XEvent      mev;
461 #endif
462 #ifdef COMPRESS_EXPOSE
463     XEvent      eev;
464     int         HasExpose = 0;
465 #endif
466     int         sync = 0;
467     XTextProperty   wm_name, icon_name;
468     Atom        wm_delete_window;
469     int         has_fg_pixel = 0, has_bg_pixel = 0;
470     int         has_colormap = 0;
471     unsigned long   gc_mask;
472 #ifndef PASS_KEYS
473     char        quit_string[10];
474 #endif
475     unsigned long   window_mask;
476
477     if (!rop_name)
478         rop_name = DEFAULT_ROP;
479     wm_name.value = (unsigned char *) argv[0];
480     wm_name.encoding = XA_STRING;
481     wm_name.format = 8;
482     wm_name.nitems = strlen ((const char *) wm_name.value) + 1;
483     icon_name = wm_name;
484     gc_mask = 0;
485     while (*++argv) {
486         if (!strcmp (*argv, "-display"))
487             dpy_name = *++argv;
488         else if (!strcmp (*argv, "-visual"))
489             vid = strtoul(*++argv, NULL, 0);
490         else if (!strcmp (*argv, "-cmap"))
491         {
492             colormap = strtoul(*++argv, NULL, 0);
493             has_colormap = 1;
494         }
495         else if (!strcmp (*argv, "-rop"))
496             rop_name = *++argv;
497         else if (!strcmp (*argv, "-fg"))
498             fg = *++argv;
499         else if (!strcmp (*argv, "-bg"))
500             bg = *++argv;
501         else if (!strcmp (*argv, "-fg_pixel"))
502         {
503             fg_pixel = strtoul(*++argv, NULL, 0);
504             has_fg_pixel = 1;
505         }
506         else if (!strcmp (*argv, "-bg_pixel"))
507         {
508             bg_pixel = strtoul(*++argv, NULL, 0);
509             has_bg_pixel = 1;
510         }
511         else if (!strcmp (*argv, "-fn"))
512             font_name = *++argv;
513         else if (!strcmp (*argv, "-pm"))
514             planemask = strtoul(*++argv, NULL, 0);
515         else if (!strcmp (*argv, "-geometry"))
516             XParseGeometry (*++argv, &x, &y, &width, &height);
517         else if (!strcmp (*argv, "-sync"))
518             sync = 1;
519         else if (!strcmp (*argv, "-bw"))
520             border_width = (unsigned int) strtoul(*++argv, NULL, 0);
521         else if (!strcmp (*argv, "-lw"))
522             line_width = strtod(*++argv, NULL);
523         else if (!strcmp (*argv, "-cap"))
524             cap_name = *++argv;
525         else if (!strcmp (*argv, "-join"))
526             join_name = *++argv;
527         else if (!strcmp (*argv, "-fill"))
528             fill_name = *++argv;
529         else if (!strcmp (*argv, "-linestyle"))
530             line_name = *++argv;
531         else if (!strcmp (*argv, "-bitmap"))
532             bitmap_file = *++argv;
533         else if (!strcmp (*argv, "-dashes"))
534         {
535             argv++;
536             ndashes = 0;
537             while (*argv && isdigit (**argv))
538                 dashes[ndashes++] = (char) atoi(*argv++);
539             argv--;
540         }
541 #ifdef MATCH_ARGUMENT
542         else if (i = MatchArgument (argv))
543             argv += i - 1;
544 #endif
545         else if (!strcmp (*argv, "-root"))
546             root = strtoul (*++argv, NULL, 0);
547         else
548             Usage (*init_argv);
549     }
550     sizeHints.flags = 0;
551     wmHints.flags = InputHint;
552     wmHints.input = True;
553     dpy = XOpenDisplay (dpy_name);
554     if (!dpy)
555         Error ("Can't open display", "");
556     if (sync)
557         XSynchronize (dpy, sync);
558     screen = DefaultScreen (dpy);
559     if (!root)
560         root = RootWindow (dpy, screen);
561     window_mask = CWBackPixel|CWBorderPixel|CWEventMask;
562     if (!has_colormap)
563         colormap = DefaultColormap (dpy, screen);
564     else
565     {
566         window_mask |= CWColormap;
567         attr.colormap = colormap;
568     }
569     visual = DefaultVisual (dpy, screen);
570     depth = DefaultDepth (dpy, screen);
571     if (vid)
572     {
573         XVisualInfo vi, *vi_ret;
574         int         n;
575
576         vi.visualid = vid;
577         vi.screen = screen;
578         vi_ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
579                                  &vi, &n);
580         if (vi_ret)
581         {
582             visual = vi_ret->visual;
583             if (!has_colormap)
584             {
585                 colormap = XCreateColormap (dpy, root, visual, AllocNone);
586                 window_mask |= CWColormap;
587                 attr.colormap = colormap;
588             }
589             depth = vi_ret->depth;
590         }
591     }
592     if (!has_fg_pixel)
593         fg_pixel = BlackPixel (dpy, screen);
594     if (!has_bg_pixel)
595         bg_pixel = WhitePixel (dpy, screen);
596     if (fg)
597     {
598         if (!XAllocNamedColor (dpy, colormap, fg, &hard, &exact))
599             Error ("Can't allocate fg pixel %s", fg);
600         fg_pixel = hard.pixel;
601     }
602     if (bg)
603     {
604         if (!XAllocNamedColor (dpy, colormap, bg, &hard, &exact))
605             Error ("Can't allocate bg pixel %s", bg);
606         bg_pixel = hard.pixel;
607     }
608     attr.background_pixel = bg_pixel;
609     attr.border_pixel = fg_pixel;
610     attr.event_mask =
611         ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|MOTION|EXTRA_EVENTS;
612 //    attr.override_redirect = True;
613 //    window_mask |= CWOverrideRedirect;
614     wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
615     win = XCreateWindow (dpy, root, x, y, width, height, border_width,
616                          depth, InputOutput,
617                          visual,
618                          window_mask,
619                          &attr);
620 #ifdef PASS_SETUP
621     Setup (dpy, win);
622 #endif
623     XSetWMProperties (dpy, win,
624                       &wm_name, &icon_name,
625                       init_argv, argc,
626                       &sizeHints, &wmHints, 0);
627     XSetWMProtocols (dpy, win, &wm_delete_window, 1);
628     default_rop =  RopNameToRop (rop_name);
629     if (line_width) 
630     {
631         gcv.line_width = (int) line_width;
632         gc_mask |= GCLineWidth;
633     }
634     if (cap_name)
635     {
636         gcv.cap_style = MatchCapStyle (cap_name);
637         gc_mask |= GCCapStyle;
638     }
639     if (fill_name) {
640         gcv.fill_style = MatchFillStyle (fill_name);
641         gc_mask |= GCFillStyle;
642     }   
643     if (line_name) {
644         gcv.line_style = MatchLineStyle (line_name);
645         gc_mask |= GCLineStyle;
646     }
647     if (join_name) {
648         gcv.join_style = MatchJoinStyle (join_name);
649         gc_mask |= GCJoinStyle;
650     }
651     if (font_name)
652         default_font = XLoadQueryFont (dpy, font_name);
653     if (default_font) {
654         gcv.font = default_font->fid;
655         gc_mask |= GCFont;
656     }
657     gcv.function = default_rop;
658     gcv.foreground = fg_pixel;
659     gcv.background = bg_pixel;
660     gcv.plane_mask = planemask;
661     gc_mask |= GCFunction|GCForeground|GCBackground|GCPlaneMask;
662     if (bitmap_file)
663     {
664         unsigned int    b_width, b_height;
665         int             ret;
666         Pixmap          bitmap, pixmap;
667
668         ret = XReadBitmapFile (dpy, win, bitmap_file, &b_width, &b_height,
669                                &bitmap, (int *) 0, (int *) 0);
670         switch (ret) {
671         case BitmapOpenFailed:
672             Error ("Can't open bitmap file %s", bitmap_file);
673             break;
674         case BitmapFileInvalid:
675             Error ("Bitmap file %s invalid", bitmap_file);
676             break;
677         case BitmapNoMemory:
678             Error ("Out of memory reading bitmap file %s", bitmap_file);
679             break;
680         case BitmapSuccess:
681             break;
682         }
683         switch (gcv.fill_style) {
684         case FillTiled:
685             pixmap = XCreatePixmap (dpy, win, b_width, b_height, (unsigned) DefaultDepth (dpy, screen));
686             gc = XCreateGC (dpy, pixmap, GCForeground|GCBackground, &gcv);
687             XCopyPlane (dpy, bitmap, pixmap, gc, 0, 0, b_width, b_height, 0, 0, 1);
688             XFreeGC (dpy, gc);
689             XFreePixmap (dpy, bitmap);
690             gcv.tile = pixmap;
691             gc_mask |= GCTile;
692             break;
693         case FillStippled:
694         case FillOpaqueStippled:
695             gcv.stipple = bitmap;
696             gc_mask |= GCStipple;
697             break;
698         }
699     }
700     gc = XCreateGC (dpy, win, gc_mask, &gcv);
701     if (!default_font)
702         default_font = XQueryFont (dpy, XGContextFromGC(gc));
703     if (ndashes)
704         XSetDashes (dpy, gc, 0, dashes, ndashes);
705     XMapWindow (dpy, win);
706     for (;;) {
707 #ifdef TIMEOUT
708         while (current_timeout && !XEventsQueued(dpy, QueuedAfterFlush)) {
709             struct pollfd pollfd = {
710                 .fd = ConnectionNumber(dpy),
711                 .events = POLLIN
712             };
713             int r = poll(&pollfd, 1, current_timeout);
714             if (r == 0)
715                 HandleTimeout(dpy, win, gc);
716         }
717 #endif
718         XNextEvent (dpy, &ev);
719 #ifdef PASS_BUTTONS
720         if (HasMotion && ev.type != MotionNotify) {
721             HasMotion = 0;
722             HandleMotionNotify (dpy, win, gc, &mev);
723         }
724 #endif
725 #ifdef COMPRESS_EXPOSE
726         if (HasExpose && ev.type != Expose) {
727             HasExpose = 0;
728             HandleExpose (dpy, eev.xexpose.window, gc);
729         }
730 #endif
731         switch (ev.type) {
732         case Expose:
733 #ifdef COMPRESS_EXPOSE
734             if (QLength(dpy)) {
735                 eev = ev;
736                 HasExpose = 1;
737             } else if (ev.xexpose.count == 0)
738 #endif
739             
740             HandleExpose (dpy, ev.xexpose.window, gc);
741             break;
742 #ifdef PASS_KEYS
743         case KeyPress:
744             HandleKeyPress (dpy, ev.xkey.window, gc, &ev);
745             break;
746         case KeyRelease:
747             HandleKeyRelease (dpy, ev.xkey.window, gc, &ev);
748             break;
749 #else
750         case KeyPress:
751             if (XLookupString ((XKeyEvent *) &ev, quit_string, sizeof (quit_string), 0, 0) == 1) {
752                 switch (quit_string[0]) {
753                 case 'q':
754                     exit (0);
755                 case 'c':
756                     XClearArea (dpy, ev.xkey.window, 0, 0, 0, 0, True);
757                     break;
758                 }
759             }
760             break;
761 #endif
762 #ifdef PASS_BUTTONS
763         case ButtonPress:
764             HandleButtonPress (dpy, ev.xbutton.window, gc, &ev);
765             break;
766         case ButtonRelease:
767             HandleButtonRelease (dpy, ev.xbutton.window, gc, &ev);
768             break;
769         case MotionNotify:
770             if (QLength(dpy)) {
771                 mev = ev;
772                 HasMotion = 1;
773             } else {
774                 HandleMotionNotify (dpy, ev.xmotion.window, gc, &ev);
775             }
776             break;
777 #endif
778         case ClientMessage:
779             exit (0);
780 #ifdef PASS_OTHER
781         default:
782             HandleOtherEvent (dpy, win, gc, &ev);
783 #endif
784         }
785     }
786 }