Merge branch 'master' into branch-1.9
[fw/altos] / src / draw / frame.c
diff --git a/src/draw/frame.c b/src/draw/frame.c
new file mode 100644 (file)
index 0000000..45cca10
--- /dev/null
@@ -0,0 +1,786 @@
+/*
+ * Copyright © 2023 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <ctype.h>
+#include    <strings.h>
+#include    <poll.h>
+#include    <X11/Xos.h>
+#include    <X11/Xlib.h>
+#include    <X11/Xutil.h>
+#include    <X11/Xatom.h>
+
+#ifndef DEFAULT_VISUAL
+#define DEFAULT_VISUAL 0
+#endif
+
+#ifndef DEFAULT_ROP
+#define DEFAULT_ROP "copy"
+#endif
+
+#ifndef DEFAULT_WIDTH
+#define DEFAULT_WIDTH 200
+#endif
+
+#ifndef DEFAULT_HEIGHT
+#define DEFAULT_HEIGHT 200
+#endif
+
+#ifndef DEFAULT_FONT
+#define DEFAULT_FONT 0
+#endif
+
+#ifndef DEFAULT_LINE_WIDTH
+#define DEFAULT_LINE_WIDTH 0
+#endif
+
+#ifndef DEFAULT_LINE_STYLE
+#define DEFAULT_LINE_STYLE NULL
+#endif
+
+#ifndef DEFAULT_FILL_STYLE
+#define DEFAULT_FILL_STYLE NULL
+#endif
+
+#ifndef DEFAULT_CAP_STYLE
+#define DEFAULT_CAP_STYLE NULL
+#endif
+
+#ifndef DEFAULT_JOIN_STYLE
+#define DEFAULT_JOIN_STYLE NULL
+#endif
+
+#ifndef DEFAULT_DASHES
+#define DEFAULT_DASHES 0
+#define DEFAULT_NUM_DASHES 0
+#endif
+
+#ifdef ALL_MOTION
+#define MOTION PointerMotionMask
+#else
+#define MOTION ButtonMotionMask
+#endif
+
+#ifndef EXTRA_EVENTS
+#define EXTRA_EVENTS 0
+#endif
+
+unsigned long   fg_pixel;
+unsigned long   bg_pixel;
+int            default_rop;
+XFontStruct    *default_font;
+
+typedef struct _RopNames { char        *name; int  rop; } RopNameRec, *RopNamePtr;
+
+RopNameRec ropNames[] = {
+       { "clear",      GXclear,        }, /* 0 */
+       { "and",                GXand,          }, /* src AND dst */
+       { "andReverse", GXandReverse,   }, /* src AND NOT dst */
+       { "copy",               GXcopy,         }, /* src */
+       { "andInverted",        GXandInverted,  }, /* NOT src AND dst */
+       { "noop",               GXnoop,         }, /* dst */
+       { "xor",                GXxor,          }, /* src XOR dst */
+       { "or",         GXor,           }, /* src OR dst */
+       { "nor",                GXnor,          }, /* NOT src AND NOT dst */
+       { "equiv",      GXequiv,        }, /* NOT src XOR dst */
+       { "invert",     GXinvert,       }, /* NOT dst */
+       { "orReverse",  GXorReverse,    }, /* src OR NOT dst */
+       { "copyInverted",       GXcopyInverted, }, /* NOT src */
+       { "orInverted", GXorInverted,   }, /* NOT src OR dst */
+       { "nand",               GXnand,         }, /* NOT src OR NOT dst */
+       { "set",                GXset,          }, /* 1 */
+};
+
+static int
+RopNameToRop (char *name)
+{
+    int        rop;
+
+    for (rop = 0; rop < 16; rop++) {
+       if (!strcmp (name, ropNames[rop].name)) {
+           return ropNames[rop].rop;
+       }
+    }
+    return GXcopy;
+}
+
+static void
+Error (char *s, char *a)
+{
+    fprintf (stderr, s, a);
+    fprintf (stderr, "\n");
+    exit (1);
+}
+
+static int
+MatchCapStyle (char *s)
+{
+    if (!strcmp (s, "round"))
+       return CapRound;
+    if (!strcmp (s, "projecting"))
+       return CapProjecting;
+    if (!strcmp (s, "butt"))
+       return CapButt;
+    if (!strcmp (s, "notlast"))
+       return CapNotLast;
+    Error ("Unknown cap style %s", s);
+    return CapNotLast;
+}
+
+static int
+MatchJoinStyle (char *s)
+{
+    if (!strcmp (s, "round"))
+       return JoinRound;
+    if (!strcmp (s, "miter"))
+       return JoinMiter;
+    if (!strcmp (s, "bevel"))
+       return JoinBevel;
+    Error ("Unknown join style %s", s);
+    return JoinBevel;
+}
+
+static int
+MatchFillStyle(char *s)
+{
+    if (!strcmp (s, "solid"))
+       return FillSolid;
+    if (!strcmp (s, "tiled"))
+       return FillTiled;
+    if (!strcmp (s, "stippled"))
+       return FillStippled;
+    if (!strcmp (s, "opaquestippled"))
+       return FillOpaqueStippled;
+    Error ("Unknown fill style %s", s);
+    return FillSolid;
+}
+
+static int
+MatchLineStyle(char *s)
+{
+    if (!strcmp (s, "solid"))
+       return LineSolid;
+    if (!strcmp (s, "onoff"))
+       return LineOnOffDash;
+    if (!strcmp (s, "double"))
+       return LineDoubleDash;
+    Error ("Unknown line style %s", s);
+    return LineSolid;
+}
+
+void
+HandleButtonPress(Display *dpy, Window win, GC gc, XEvent *ev);
+
+void
+HandleButtonRelease(Display *dpy, Window win, GC gc, XEvent *ev);
+
+void
+HandleMotionNotify(Display *dpy, Window win, GC gc, XEvent *ev);
+
+#ifdef TRACK_POINTER
+
+#define PASS_BUTTONS
+typedef struct _Position {
+    int        seen;
+    int        start_x, start_y;
+    int        cur_x, cur_y;
+    int        end_x, end_y;
+} PositionRec, *PositionPtr;
+
+void
+Draw(Display *dpy, Window win, GC gc, PositionRec positions[5]);
+
+void
+Undraw(Display *dpy, Window win, GC gc, PositionRec positions[5]);
+
+PositionRec current_positions[5];
+
+static void
+UpdatePositions (unsigned state, int x, int y)
+{
+    int        i;
+
+    state >>= 8;
+    for (i = 0; i < 5; i++)
+    {
+       if (state & (1 << i)) {
+           current_positions[i].cur_x = x;
+           current_positions[i].cur_y = y;
+       }
+    }
+}
+
+void
+HandleButtonPress (Display *dpy, Window win, GC gc, XEvent *ev)
+{
+    XButtonEvent    *bev = (XButtonEvent *) ev;
+
+    Undraw (dpy, win, gc, current_positions);
+    current_positions[bev->button - 1].seen = 1;
+    current_positions[bev->button - 1].start_x = bev->x;
+    current_positions[bev->button - 1].start_y = bev->y;
+    current_positions[bev->button - 1].cur_x = bev->x;
+    current_positions[bev->button - 1].cur_y = bev->y;
+    current_positions[bev->button - 1].end_x = bev->x;
+    current_positions[bev->button - 1].end_y = bev->y;
+    UpdatePositions (bev->state, bev->x, bev->y);
+    Draw (dpy, win, gc, current_positions);
+}
+
+void
+HandleButtonRelease (Display *dpy, Window win, GC gc, XEvent *ev)
+{
+    XButtonEvent    *bev = (XButtonEvent *) ev;
+
+    Undraw (dpy, win, gc, current_positions);
+    UpdatePositions (bev->state, bev->x, bev->y);
+    current_positions[bev->button - 1].end_x = bev->x;
+    current_positions[bev->button - 1].end_y = bev->y;
+    Draw (dpy, win, gc, current_positions);
+}
+
+void
+HandleMotionNotify (Display *dpy, Window win, GC gc, XEvent *ev)
+{
+    XMotionEvent    *mev = (XMotionEvent *) ev;
+
+    Undraw (dpy, win, gc, current_positions);
+    UpdatePositions (mev->state, mev->x, mev->y);
+    Draw (dpy, win, gc, current_positions);
+}
+
+static inline void
+DisplayPositions (Display *dpy, Window win, GC gc, PositionRec positions[5])
+{
+    static char        text[1024];
+    static int hastext;
+    int                pos;
+    int                i;
+    int                dir, font_ascent, font_descent;
+    XCharStruct        overall;
+
+    XTextExtents (default_font, text, (int) strlen(text),
+           &dir, &font_ascent, &font_descent, &overall);
+    if (hastext)
+           XClearArea (dpy, win, 0, 0, (unsigned) overall.width, (unsigned) (font_ascent + font_descent), False);
+    pos = 0;
+    for (i = 0; i < 5; i++)
+    {
+       if (positions[i].seen)
+       {
+           if (pos)
+               text[pos++] = ',';
+           sprintf (text + pos, "%1d: (%4d,%4d),(%4d,%4d)",
+               i, positions[i].start_x, positions[i].start_y,
+               positions[i].cur_x, positions[i].cur_y);
+           pos = (int) strlen (text);
+       }
+    }
+    XDrawString (dpy, win, gc, 0, font_ascent, text, pos);
+    hastext = 1;
+}
+
+#endif
+
+static inline void
+SetToFg (Display *dpy, GC gc)
+{
+    XSetForeground (dpy, gc, fg_pixel);
+    XSetFunction (dpy, gc, default_rop);
+}
+
+static inline void
+SetToBg (Display *dpy, GC gc)
+{
+    XSetForeground (dpy, gc, bg_pixel);
+    XSetFunction (dpy, gc, GXcopy);
+}
+static void
+Usage (char *program)
+{
+    int        i;
+    fprintf (stderr, "Usage: %s\n", program);
+    fprintf (stderr, "\t-display <display-name>\n");
+    fprintf (stderr, "\t-rop {");
+    for (i = 0; i < 16; i++) {
+       fprintf (stderr, "%s", ropNames[i].name);
+       if (i == 15)
+           fprintf (stderr, "}\n");
+       else if (i % 4 == 3)
+           fprintf (stderr, ",\n\t\t");
+       else
+           fprintf (stderr, ", ");
+    }
+    fprintf (stderr, "\t-fg <fg color name>\n");
+    fprintf (stderr, "\t-bg <bg color name>\n");
+    fprintf (stderr, "\t-fg_pixel <fg pixel value>\n");
+    fprintf (stderr, "\t-bg_pixel <bg pixel value>\n");
+    fprintf (stderr, "\t-fn <font name>\n");
+    fprintf (stderr, "\t-geometry <geometry>\n");
+    fprintf (stderr, "\t-lw <line width>\n");
+    fprintf (stderr, "\t-cap {round, projecting, butt, notlast}\n");
+    fprintf (stderr, "\t-join {round, miter, bevel}\n");
+    fprintf (stderr, "\t-fill {solid, tiled, stippled, opaquestippled}\n");
+    fprintf (stderr, "\t-linestyle {solid, onoff, double}\n");
+    fprintf (stderr, "\t-bitmap <bitmap file name>\n");
+    fprintf (stderr, "\t-dashes [<dash value> ...]\n");
+    exit (1);
+}
+
+char    *dpy_name;
+char    *rop_name;
+char    *fg;
+char    *bg;
+char    *bitmap_file;
+char   *cap_name = DEFAULT_CAP_STYLE;
+char   *join_name = DEFAULT_JOIN_STYLE;
+char   *fill_name = DEFAULT_FILL_STYLE;
+char   *line_name = DEFAULT_LINE_STYLE;
+char   *font_name = DEFAULT_FONT;
+double line_width = DEFAULT_LINE_WIDTH;
+char   dashes[256] = { DEFAULT_DASHES };
+int    ndashes = DEFAULT_NUM_DASHES;
+VisualID    vid = DEFAULT_VISUAL;
+unsigned long  planemask = ~0UL;
+Colormap    colormap;
+Visual     *visual;
+int        depth;
+Window  root = 0;
+int        screen;
+#ifdef TIMEOUT
+int    current_timeout = TIMEOUT;
+#endif
+
+void
+Setup(Display *dpy, Window win);
+
+void
+HandleExpose(Display *dpy, Window win, GC gc);
+
+void
+HandleKeyPress(Display *dpy, Window win, GC gc, XEvent *ev);
+
+void
+HandleKeyRelease(Display *dpy, Window win, GC gc, XEvent *ev);
+
+void
+HandleTimeout(Display *dpy, Window win, GC gc);
+
+#include <X11/extensions/Xrender.h>
+
+XRenderColor   renderBlack = { 0, 0, 0, 0xffff };
+XRenderColor   renderWhite = { 0xffff, 0xffff, 0xffff, 0xffff };
+XRenderColor   renderRed = { 0xffff, 0, 0, 0xffff };
+XRenderColor   renderGreen = { 0, 0xffff, 0, 0xffff };
+XRenderColor   renderBlue = { 0, 0, 0xffff, 0xffff };
+
+XRenderColor   renderClearRed = { 0x8000, 0, 0, 0x8000 };
+XRenderColor   renderClearGreen = { 0, 0x8000, 0, 0x8000 };
+XRenderColor   renderClearBlue = { 0, 0, 0x8000, 0x8000 };
+
+static inline Picture
+GetPicture (Display *dpy, Window w)
+{
+    static Picture p;
+
+    if (!p)
+       p = XRenderCreatePicture (dpy, w, 
+                                 XRenderFindVisualFormat (dpy, visual),
+                                 0, 0);
+    return p;
+}
+
+static inline Picture
+GetSrc (Display *dpy, XRenderColor *color)
+{
+    static Picture     p;
+    static Pixmap      pix;
+    static XRenderColor        lastColor;
+    XRenderPictFormat  *f;
+    XRenderPictureAttributes   attr;
+
+    if (p && !memcmp (color, &lastColor, sizeof (XRenderColor)))
+       return p;
+    if (!p)
+    {
+       f = XRenderFindStandardFormat (dpy, PictStandardARGB32);
+       pix = XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
+                            1, 1, (unsigned) f->depth);
+       attr.repeat = True;
+       p = XRenderCreatePicture (dpy, pix, f, CPRepeat, &attr);
+       XFreePixmap (dpy, pix);
+    }
+    XRenderFillRectangle (dpy, PictOpSrc, p, color, 0, 0, 1, 1);
+    lastColor = *color;
+    return p;
+}
+
+static inline XRenderPictFormat *
+GetMask (Display *dpy)
+{
+    return XRenderFindStandardFormat (dpy, PictStandardA8);
+}
+
+int
+main (int argc, char **argv)
+{
+    Display *dpy;
+    Window  win;
+    GC     gc;
+    char    **init_argv = argv;
+    XSetWindowAttributes    attr;
+    XColor  hard, exact;
+    int            x = 0, y = 0;
+    unsigned int    width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT;
+    unsigned int           border_width = 1u;
+    XSizeHints sizeHints;
+    XWMHints   wmHints;
+    XEvent     ev;
+    XGCValues  gcv;
+#ifdef MATCH_ARGUMENT
+    int                i;
+#endif
+#ifdef PASS_BUTTONS
+    int                HasMotion = 0;
+    XEvent     mev;
+#endif
+#ifdef COMPRESS_EXPOSE
+    XEvent     eev;
+    int                HasExpose = 0;
+#endif
+    int                sync = 0;
+    XTextProperty   wm_name, icon_name;
+    Atom       wm_delete_window;
+    int                has_fg_pixel = 0, has_bg_pixel = 0;
+    int                has_colormap = 0;
+    unsigned long   gc_mask;
+#ifndef PASS_KEYS
+    char       quit_string[10];
+#endif
+    unsigned long   window_mask;
+
+    if (!rop_name)
+       rop_name = DEFAULT_ROP;
+    wm_name.value = (unsigned char *) argv[0];
+    wm_name.encoding = XA_STRING;
+    wm_name.format = 8;
+    wm_name.nitems = strlen ((const char *) wm_name.value) + 1;
+    icon_name = wm_name;
+    gc_mask = 0;
+    while (*++argv) {
+       if (!strcmp (*argv, "-display"))
+           dpy_name = *++argv;
+       else if (!strcmp (*argv, "-visual"))
+           vid = strtoul(*++argv, NULL, 0);
+       else if (!strcmp (*argv, "-cmap"))
+       {
+           colormap = strtoul(*++argv, NULL, 0);
+           has_colormap = 1;
+       }
+       else if (!strcmp (*argv, "-rop"))
+           rop_name = *++argv;
+       else if (!strcmp (*argv, "-fg"))
+           fg = *++argv;
+       else if (!strcmp (*argv, "-bg"))
+           bg = *++argv;
+       else if (!strcmp (*argv, "-fg_pixel"))
+       {
+           fg_pixel = strtoul(*++argv, NULL, 0);
+           has_fg_pixel = 1;
+       }
+       else if (!strcmp (*argv, "-bg_pixel"))
+       {
+           bg_pixel = strtoul(*++argv, NULL, 0);
+           has_bg_pixel = 1;
+       }
+       else if (!strcmp (*argv, "-fn"))
+           font_name = *++argv;
+       else if (!strcmp (*argv, "-pm"))
+           planemask = strtoul(*++argv, NULL, 0);
+       else if (!strcmp (*argv, "-geometry"))
+           XParseGeometry (*++argv, &x, &y, &width, &height);
+       else if (!strcmp (*argv, "-sync"))
+           sync = 1;
+       else if (!strcmp (*argv, "-bw"))
+           border_width = (unsigned int) strtoul(*++argv, NULL, 0);
+       else if (!strcmp (*argv, "-lw"))
+           line_width = strtod(*++argv, NULL);
+       else if (!strcmp (*argv, "-cap"))
+           cap_name = *++argv;
+       else if (!strcmp (*argv, "-join"))
+           join_name = *++argv;
+       else if (!strcmp (*argv, "-fill"))
+           fill_name = *++argv;
+       else if (!strcmp (*argv, "-linestyle"))
+           line_name = *++argv;
+       else if (!strcmp (*argv, "-bitmap"))
+           bitmap_file = *++argv;
+       else if (!strcmp (*argv, "-dashes"))
+       {
+           argv++;
+           ndashes = 0;
+           while (*argv && isdigit (**argv))
+               dashes[ndashes++] = (char) atoi(*argv++);
+           argv--;
+       }
+#ifdef MATCH_ARGUMENT
+       else if (i = MatchArgument (argv))
+           argv += i - 1;
+#endif
+       else if (!strcmp (*argv, "-root"))
+           root = strtoul (*++argv, NULL, 0);
+       else
+           Usage (*init_argv);
+    }
+    sizeHints.flags = 0;
+    wmHints.flags = InputHint;
+    wmHints.input = True;
+    dpy = XOpenDisplay (dpy_name);
+    if (!dpy)
+       Error ("Can't open display", "");
+    if (sync)
+       XSynchronize (dpy, sync);
+    screen = DefaultScreen (dpy);
+    if (!root)
+       root = RootWindow (dpy, screen);
+    window_mask = CWBackPixel|CWBorderPixel|CWEventMask;
+    if (!has_colormap)
+       colormap = DefaultColormap (dpy, screen);
+    else
+    {
+       window_mask |= CWColormap;
+       attr.colormap = colormap;
+    }
+    visual = DefaultVisual (dpy, screen);
+    depth = DefaultDepth (dpy, screen);
+    if (vid)
+    {
+       XVisualInfo vi, *vi_ret;
+       int         n;
+
+       vi.visualid = vid;
+       vi.screen = screen;
+       vi_ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
+                                &vi, &n);
+       if (vi_ret)
+       {
+           visual = vi_ret->visual;
+           if (!has_colormap)
+           {
+               colormap = XCreateColormap (dpy, root, visual, AllocNone);
+               window_mask |= CWColormap;
+               attr.colormap = colormap;
+           }
+           depth = vi_ret->depth;
+       }
+    }
+    if (!has_fg_pixel)
+       fg_pixel = BlackPixel (dpy, screen);
+    if (!has_bg_pixel)
+       bg_pixel = WhitePixel (dpy, screen);
+    if (fg)
+    {
+       if (!XAllocNamedColor (dpy, colormap, fg, &hard, &exact))
+           Error ("Can't allocate fg pixel %s", fg);
+       fg_pixel = hard.pixel;
+    }
+    if (bg)
+    {
+       if (!XAllocNamedColor (dpy, colormap, bg, &hard, &exact))
+           Error ("Can't allocate bg pixel %s", bg);
+       bg_pixel = hard.pixel;
+    }
+    attr.background_pixel = bg_pixel;
+    attr.border_pixel = fg_pixel;
+    attr.event_mask =
+       ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|MOTION|EXTRA_EVENTS;
+//    attr.override_redirect = True;
+//    window_mask |= CWOverrideRedirect;
+    wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+    win = XCreateWindow (dpy, root, x, y, width, height, border_width,
+                        depth, InputOutput,
+                        visual,
+                        window_mask,
+                        &attr);
+#ifdef PASS_SETUP
+    Setup (dpy, win);
+#endif
+    XSetWMProperties (dpy, win,
+                     &wm_name, &icon_name,
+                     init_argv, argc,
+                     &sizeHints, &wmHints, 0);
+    XSetWMProtocols (dpy, win, &wm_delete_window, 1);
+    default_rop =  RopNameToRop (rop_name);
+    if (line_width) 
+    {
+       gcv.line_width = (int) line_width;
+       gc_mask |= GCLineWidth;
+    }
+    if (cap_name)
+    {
+       gcv.cap_style = MatchCapStyle (cap_name);
+       gc_mask |= GCCapStyle;
+    }
+    if (fill_name) {
+       gcv.fill_style = MatchFillStyle (fill_name);
+       gc_mask |= GCFillStyle;
+    }  
+    if (line_name) {
+       gcv.line_style = MatchLineStyle (line_name);
+       gc_mask |= GCLineStyle;
+    }
+    if (join_name) {
+       gcv.join_style = MatchJoinStyle (join_name);
+       gc_mask |= GCJoinStyle;
+    }
+    if (font_name)
+       default_font = XLoadQueryFont (dpy, font_name);
+    if (default_font) {
+       gcv.font = default_font->fid;
+       gc_mask |= GCFont;
+    }
+    gcv.function = default_rop;
+    gcv.foreground = fg_pixel;
+    gcv.background = bg_pixel;
+    gcv.plane_mask = planemask;
+    gc_mask |= GCFunction|GCForeground|GCBackground|GCPlaneMask;
+    if (bitmap_file)
+    {
+       unsigned int    b_width, b_height;
+       int             ret;
+       Pixmap          bitmap, pixmap;
+
+       ret = XReadBitmapFile (dpy, win, bitmap_file, &b_width, &b_height,
+                              &bitmap, (int *) 0, (int *) 0);
+       switch (ret) {
+       case BitmapOpenFailed:
+           Error ("Can't open bitmap file %s", bitmap_file);
+           break;
+       case BitmapFileInvalid:
+           Error ("Bitmap file %s invalid", bitmap_file);
+           break;
+       case BitmapNoMemory:
+           Error ("Out of memory reading bitmap file %s", bitmap_file);
+           break;
+       case BitmapSuccess:
+           break;
+       }
+       switch (gcv.fill_style) {
+       case FillTiled:
+           pixmap = XCreatePixmap (dpy, win, b_width, b_height, (unsigned) DefaultDepth (dpy, screen));
+           gc = XCreateGC (dpy, pixmap, GCForeground|GCBackground, &gcv);
+           XCopyPlane (dpy, bitmap, pixmap, gc, 0, 0, b_width, b_height, 0, 0, 1);
+           XFreeGC (dpy, gc);
+           XFreePixmap (dpy, bitmap);
+           gcv.tile = pixmap;
+           gc_mask |= GCTile;
+           break;
+       case FillStippled:
+       case FillOpaqueStippled:
+           gcv.stipple = bitmap;
+           gc_mask |= GCStipple;
+           break;
+       }
+    }
+    gc = XCreateGC (dpy, win, gc_mask, &gcv);
+    if (!default_font)
+       default_font = XQueryFont (dpy, XGContextFromGC(gc));
+    if (ndashes)
+       XSetDashes (dpy, gc, 0, dashes, ndashes);
+    XMapWindow (dpy, win);
+    for (;;) {
+#ifdef TIMEOUT
+       while (current_timeout && !XEventsQueued(dpy, QueuedAfterFlush)) {
+           struct pollfd pollfd = {
+               .fd = ConnectionNumber(dpy),
+               .events = POLLIN
+           };
+           int r = poll(&pollfd, 1, current_timeout);
+           if (r == 0)
+               HandleTimeout(dpy, win, gc);
+       }
+#endif
+       XNextEvent (dpy, &ev);
+#ifdef PASS_BUTTONS
+       if (HasMotion && ev.type != MotionNotify) {
+           HasMotion = 0;
+           HandleMotionNotify (dpy, win, gc, &mev);
+       }
+#endif
+#ifdef COMPRESS_EXPOSE
+       if (HasExpose && ev.type != Expose) {
+           HasExpose = 0;
+           HandleExpose (dpy, eev.xexpose.window, gc);
+       }
+#endif
+       switch (ev.type) {
+       case Expose:
+#ifdef COMPRESS_EXPOSE
+           if (QLength(dpy)) {
+               eev = ev;
+               HasExpose = 1;
+           } else if (ev.xexpose.count == 0)
+#endif
+           
+           HandleExpose (dpy, ev.xexpose.window, gc);
+           break;
+#ifdef PASS_KEYS
+       case KeyPress:
+           HandleKeyPress (dpy, ev.xkey.window, gc, &ev);
+           break;
+       case KeyRelease:
+           HandleKeyRelease (dpy, ev.xkey.window, gc, &ev);
+           break;
+#else
+       case KeyPress:
+           if (XLookupString ((XKeyEvent *) &ev, quit_string, sizeof (quit_string), 0, 0) == 1) {
+               switch (quit_string[0]) {
+               case 'q':
+                   exit (0);
+               case 'c':
+                   XClearArea (dpy, ev.xkey.window, 0, 0, 0, 0, True);
+                   break;
+               }
+           }
+           break;
+#endif
+#ifdef PASS_BUTTONS
+       case ButtonPress:
+           HandleButtonPress (dpy, ev.xbutton.window, gc, &ev);
+           break;
+       case ButtonRelease:
+           HandleButtonRelease (dpy, ev.xbutton.window, gc, &ev);
+           break;
+       case MotionNotify:
+           if (QLength(dpy)) {
+               mev = ev;
+               HasMotion = 1;
+           } else {
+               HandleMotionNotify (dpy, ev.xmotion.window, gc, &ev);
+           }
+           break;
+#endif
+       case ClientMessage:
+           exit (0);
+#ifdef PASS_OTHER
+       default:
+           HandleOtherEvent (dpy, win, gc, &ev);
+#endif
+       }
+    }
+}