/* xlib.c -- raw XLib manipulation

   version -0.2

   Copyright (C) 2000 merlin <merlin@merlin.org>

   With the unwitting assistance of John Harper.

   Copyright (C) 2000 John Harper <john@dcs.warwick.ac.uk>

   This 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, or (at your option)
   any later version.

   This 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 sawmill; see the file COPYING.   If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

*                                    *
*                           #        *
*                           #        *
*                      ######        *
*                 ###########        *
*             ##########             *
*        ##########                  *
*          ####                      *
*              ####                  *
*           ##########               *
*       ##########                   *
*            ####                    * This Software is Not Good Software.
*                 ###                * 
*                     ####  #        * The Tao of Sawmill is that
*                          ##        * a Window Manager Manages Windows.
*                           #        *
*       #                            * That is How It Should Be. 
*       #                            *
*       # ###                        * That is Right.
*             ###                    * 
*              #  ###                * This Software violates the Tao of
*              #      ###            * Sawmill by making the window manager
*              #      #######        * do what it should not.
*              #  #########          *
*             ########               * The Purity of Sawmill is Sullied by
*       # #########                  * this Software.
*       ######                       *
*       ###                          * This Software Should Not Be.
*       #                            *
*       #                   #        * Do not use this Software.
*       #####################        *
*       #####################        * Merely observe, weep, gnash of your
*       #          ##       #        * teeth and pull of your hair.
*               #####       #        *
*            ########       #        * --
*       # #######   #       #        *
*       ######      ##     ##        * Use instead a real pager based on
*       ###          #######         * stph or somesuch.
*       #              ###           *
*       #                   #        * --
*       #####################        *
*       #                 ###        * Let me reiterate one more time
*                      ######        * before I'm drunk again:
*                   #######          *
*                #######             * This software is a retrograde step.
*             #######                *
*           #######                  * The Purity And Lightness of Sawmill
*        ######             #        * is its Greatness.
*       #####################        *
*                                    * A Window Manager should not include
*       #                   #        * Applications such as this.
*       #####################        *
*       #####################        * Discrete applications can do a much
*       #                   #        * better job.
*                                    *
*       #                   #        * This Software is a return to the old
*       #####################        * ways of proprietary gadgets on
*       #                 ###        * bloated, unstable window managers.
*                      ######        *
*                   #######          * --
*                #######             *
*             #######                * For the love of all that is good,
*           #######                  * turn back now.
*        ######             #        *
*       #####################        *
*                                    *
*               #####                *
*           #############            *
*         #################          *
*        ###              ##         *
*        #                  #        *
*       #                   #        *
*        #                  #        *
*        ##               ##         *
*         #########      ####        *
*            ######                  *
*                 #                  *
*                                    *

   *********************
   ** HERE BE DRAGONS **
   *********************

   This code contains horrendous hacks. It introduces the high
   probability of crashing your Window Manager and Rendering it
   Unstable and Destroying your Valuable Work and Property.

   Tnis is unlikely to work with earlier or later versions of
   Sawmill.

   Sawmill was not written with code of this nature on mind.
   
   More to the point, Sawmill was written with the express
   intention of this NOT EVER being done. As a result, this
   Software introduces the EXTREME PROBABILITY of FAILURE that
   DOES NOT EXIST in Sawmill itself.
   
   ******************
   ** INSTALLATION **
   ******************

   I assume that you have a recent copy of the Sawmill
   source unpacked somewhere.

   Copy this file into the `src' directory.

   Edit `src/Makefile.in' (and `src/Makefile', if you have
   already run `configure').

   Add `xlib.c' to the definition of `DL_SRCS':
     DL_SRCS := gradient.c ... xlib.c

   Compile and install Sawmill.
     make
     make install

   Restart Sawmill.
   
   ******************
   ** HERE BE BUGS **
   ******************

   Many XLib features are unimplemented and misimplemented.

   My understanding of rep modules is incomplete and erroneous.

   In order to support managed windows I introduced many hacks with
   UNKNOWN CONSEQUENCES.

   This code allows you to emulate being a distinct X application when you
   are in fact just a tiny part of a Window Manager that knows NOTHING
   about you. As a result, expect Window Management not to work as it
   should, and expect Your Application not to work as it should. You won't
   get events that you expect, you will get events that you don't and the
   Window Manager will simply not operate 100% as it should.

   In particular, if you create a managed window then it will probably be
   useless to you; you'll want to cover it with a child.

   One day I'll chop this off so it is a separate rep module that allows
   you to write standalone XLib applications that are not bastard,
   deformed monstrosities sprouting from the side of something beautiful.

*/

/* AIX requires this to be the first thing in the file.  */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
   char *alloca ();
#   endif
#  endif
# endif
#endif
   
#include "sawmill.h"
#include <X11/Xresource.h>
#include <X11/extensions/Xdbe.h>

/* merlin's horrible defines */

#define LIST_PRE(A,B) A = Fcons (B, A)
#define ALIST_PRE(A,B,C) A = Fcons (Fcons (B, C), A)

#define nDECLARE(index,arg, assert) {\
  rep_DECLARE (index, args, rep_CONSP (args));\
  arg = rep_CAR (args);\
  args = rep_CDR (args);\
  rep_DECLARE (index, arg, assert);\
}

/* and the rest */

static int have_dbe;

/* An allocated xlib-gc */
typedef struct lisp_xlib_gc {
    repv car;
    struct lisp_xlib_gc *next;
    GC gc;
    Window id;
} Lisp_XLib_GC;

#define XLIB_XGCP(v)	rep_CELL16_TYPEP(v, xlib_gc_type)
#define XLIB_GCP(v)	(XLIB_XGCP(v) && VXLIB_GC(v)->gc != 0)
#define XLIB_VALID_GCP(v,id) XLIB_GCP(v)
#define VXLIB_GC(v)	((Lisp_XLib_GC *)rep_PTR(v))

/* An allocated xlib-window */
typedef struct lisp_xlib_window {
    repv car;
    struct lisp_xlib_window *next;
    Window id;
    XdbeBackBuffer back_buffer;
    repv event_handler;
    repv plist;
} Lisp_XLib_Window;

#define XLIB_XWINDOWP(v) rep_CELL16_TYPEP(v, xlib_window_type)
#define XLIB_WINDOWP(v)  (XLIB_XWINDOWP(v) && VXLIB_WINDOW(v)->id != 0)
#define VXLIB_WINDOW(v)  ((Lisp_XLib_Window *)rep_PTR(v))

static Lisp_XLib_GC *xlib_gc_list = NULL;
int xlib_gc_type;

static Lisp_XLib_Window *xlib_window_list = NULL;
int xlib_window_type;

static XID xlib_window_context;

/* configure-window */
DEFSYM(x, "x");
DEFSYM(y, "y");
DEFSYM(sibling, "sibling");
DEFSYM(stack_mode, "stack-mode");

/* restacking */
DEFSYM(raise_lowest, "raise-lowest");
DEFSYM(lower_highest, "lower-highest");

/* set-window-attributes */
DEFSYM(border_width, "border-width");
DEFSYM(border_color, "border-color");
DEFSYM(override_redirect, "override-redirect");
DEFSYM(save_under, "save-under");
DEFSYM(event_mask, "event-mask");
DEFSYM(parent, "parent");

/* event encoding */
DEFSYM(serial, "serial");
DEFSYM(send_event, "send-event");
DEFSYM(window, "window");
DEFSYM(subwindow, "subwindow");
DEFSYM(time, "time");
DEFSYM(x_root, "x-root");
DEFSYM(y_root, "y-root");
DEFSYM(state, "state");
DEFSYM(keycode, "keycode");
DEFSYM(same_screen, "same-screen");
DEFSYM(button, "button");
DEFSYM(is_hint, "is-hint");
DEFSYM(focus, "focus");
DEFSYM(mode, "mode");
DEFSYM(detail, "detail");
DEFSYM(count, "count");
DEFSYM(message_type, "message-type");
DEFSYM(format, "format");
DEFSYM(data, "data");

/* polygons */
DEFSYM(convex, "convex");
DEFSYM(non_convex, "non-convex");

static Atom
xlib_atom_symbol (repv symbol) {
  return XInternAtom (dpy, rep_STR (rep_SYM (symbol)->name), False);
}

typedef struct {
    unsigned int value;
    char *str;
} x_value_str;

static repv
x_value_match (unsigned int value, x_value_str *match) {
    while (match->str) {
        if (value == match->value)
            return Fintern (rep_string_dup (match->str), Qnil);
        ++ match;
    }
    return Qnil;
}

static repv
x_valuemask_match (unsigned int value, x_value_str *match) {
    repv result = Qnil;
    while (match->str) {
        if (value & match->value)
            LIST_PRE (result, Fintern (rep_string_dup (match->str), Qnil));
        ++ match;
    }
    return result;
}

typedef struct {
    char *str;
    unsigned int value;
} x_str_value;

static int
x_symbol_match (repv symbol, x_str_value *match) {
    char *tmp;
    if (!rep_SYMBOLP (symbol))
      return -1;
    tmp = rep_STR (rep_SYM (symbol)->name);
    while (match->str) {
        if (!strcmp (match->str, tmp))
            return match->value;
        ++ match;
    }
    return -1;
}

static Window
window_from_arg (repv arg)
{
    if (rep_INTEGERP (arg))
	return rep_get_long_uint (arg);
    else if (XLIB_WINDOWP (arg))
    {
	if (VXLIB_WINDOW(arg)->back_buffer)
	    return VXLIB_WINDOW(arg)->back_buffer;
	else
	    return VXLIB_WINDOW(arg)->id;
    }
    else if (WINDOWP(arg) && VWIN(arg)->id != 0)
	return VWIN(arg)->id;
    else if (PARTP(arg) && VPART(arg)->id != 0)
	return VPART(arg)->id;
    else
	return 0;
}


/* GC Functions */

static long
xlib_gc_parse_attrs (XGCValues *values, repv attrs)
{
    long valueMask = 0;

    while (rep_CONSP (attrs)) {
        repv tem = rep_CAR (attrs);

        if (rep_CONSP (tem)) {
            repv car = rep_CAR (tem);
            if ((car == Qforeground) && COLORP(rep_CDR(tem))) {
                values->foreground = VCOLOR(rep_CDR(tem))->pixel;
                valueMask |= GCForeground;
            } else if ((car == Qbackground) && COLORP(rep_CDR(tem))) {
                values->background = VCOLOR(rep_CDR(tem))->pixel;
                valueMask |= GCBackground;
            }
        }

        attrs = rep_CDR (attrs);
    }

    return valueMask;
}

DEFUN("xlib-create-gc", Fxlib_create_gc, Sxlib_create_gc, (repv window, repv attrs), rep_Subr2) /*
::doc:xlib-create-gc::
xlib-create-gc WINDOW ATTRS

Creates a new GC for the specified window. ATTRS should be an alist
mapping attributes to values. Known attributes are `foreground' and
`background'.
::end:: */
{
    Window id = window_from_arg (window);
    Lisp_XLib_GC *g;
    GC gc;
    XGCValues values;
    long valueMask;

    if (dpy == 0)
	return Qnil;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE2(attrs, rep_LISTP);

    valueMask = xlib_gc_parse_attrs (&values, attrs);
    gc = XCreateGC (dpy, id, valueMask, &values);

    g = rep_ALLOC_CELL(sizeof(Lisp_XLib_GC));
    rep_data_after_gc += sizeof (Lisp_XLib_GC);
    g->car = xlib_gc_type;
    g->next = xlib_gc_list;
    xlib_gc_list = g;
    g->gc = gc;
    g->id = id;

    return rep_VAL(g);
}

DEFUN("xlib-change-gc", Fxlib_change_gc, Sxlib_change_gc, (repv gc, repv attrs), rep_Subr2) /*
::doc:xlib-change-gc::
xlib-change-gc X-GC ATTRS

Sets attributes of the X-GC. ATTRS should be a list of cons cells mapping
attributes to values. Known attributes are `foreground' and `background'.
::end:: */
{
    XGCValues values;
    long valueMask;

    rep_DECLARE1(gc, XLIB_GCP);
    rep_DECLARE2(attrs, rep_LISTP);

    valueMask = xlib_gc_parse_attrs (&values, attrs);

    if (valueMask)
      XChangeGC (dpy, VXLIB_GC(gc)->gc, valueMask, &values);

    return Qt;
}

DEFUN("xlib-free-gc", Fxlib_free_gc, Sxlib_free_gc, (repv gc), rep_Subr1) /*
::doc:xlib-free-gc::
xlib-free-gc X-GC

Destroy the X-GC.
::end:: */
{
    rep_DECLARE1(gc, XLIB_GCP);

    XFreeGC (dpy, VXLIB_GC(gc)->gc);
    VXLIB_GC(gc)->gc = NULL;

    return Qt;
}

DEFUN("xlib-gc-p", Fxlib_gc_p, Sxlib_gc_p, (repv gc), rep_Subr1) /*
::doc:xlib-gc-p::
xlib-gcp ARG

Return t if ARG is a X-GC object.
::end:: */
{
    return XLIB_GCP(gc) ? Qt : Qnil;
}


/* Window functions */

static inline repv
xlib_window_from_id (Window id)
{
    repv win;
    return XFindContext (dpy, id, xlib_window_context,
			 (XPointer *) &win) ? Qnil : win;
}

static x_str_value x_stack_mode_matches[] = {
    { "above", Above },
    { "below", Below },
    { "top-if", TopIf },
    { "bottom-if", BottomIf },
    { "opposite", Opposite },
    { 0, 0 }
};

static long
xlib_window_parse_changes (XWindowChanges *changes, repv attrs)
{
    long changesMask = 0;

    while (rep_CONSP (attrs)) {
        repv tem = rep_CAR (attrs);

        if (rep_CONSP (tem)) {
            repv car = rep_CAR (tem);
            if ((car == Qx) && rep_INTP(rep_CDR(tem))) {
                changes->x = rep_INT(rep_CDR(tem));
                changesMask |= CWX;
            } else if ((car == Qy) && rep_INTP(rep_CDR(tem))) {
                changes->y = rep_INT(rep_CDR(tem));
                changesMask |= CWY;
            } else if ((car == Qwidth) && rep_INTP(rep_CDR(tem))) {
                changes->width = rep_INT(rep_CDR(tem));
                changesMask |= CWWidth;
            } else if ((car == Qheight) && rep_INTP(rep_CDR(tem))) {
                changes->height = rep_INT(rep_CDR(tem));
                changesMask |= CWHeight;
            } else if ((car == Qborder_width) && rep_INTP(rep_CDR(tem))) {
                changes->border_width = rep_INT(rep_CDR(tem));
                changesMask |= CWBorderWidth;
            } else if (car == Qsibling) {
                Window sibling = window_from_arg (rep_CDR (tem));
                if (sibling) {
                  changes->sibling = sibling;
                  changesMask |= CWSibling;
                }
            } else if (car == Qstack_mode) {
                int stack_mode = x_symbol_match (rep_CDR (tem), x_stack_mode_matches);
                if (stack_mode != -1) {
                  changes->stack_mode = stack_mode;
                  changesMask |= CWStackMode;
                }
            }
        }

        attrs = rep_CDR (attrs);
    }

    return changesMask;
}

static x_str_value x_event_mask_matches[] = {
    { "key-press", KeyPressMask },
    { "key-release", KeyReleaseMask },
    { "button-press", ButtonPressMask },
    { "button-release", ButtonReleaseMask },
    { "enter-window", EnterWindowMask },
    { "leave-window", LeaveWindowMask },
    { "pointer-motion", PointerMotionMask },
    { "pointer-motion-hint", PointerMotionHintMask },
    { "button-1-motion", Button1MotionMask },
    { "button-2-motion", Button2MotionMask },
    { "button-3-motion", Button3MotionMask },
    { "button-4-motion", Button4MotionMask },
    { "button-5-motion", Button5MotionMask },
    { "button-motion", ButtonMotionMask },
    { "keymap-state", KeymapStateMask },
    { "exposure", ExposureMask },
    { "visibility-change", VisibilityChangeMask },
    { "structure-notify", StructureNotifyMask },
    { "resize-redirect", ResizeRedirectMask },
    { "substructure-notify", SubstructureNotifyMask },
    { "substructure-redirect", SubstructureRedirectMask },
    { "focus-change", FocusChangeMask },
    { "property-change", PropertyChangeMask },
    { "colormap-change", ColormapChangeMask },
    { "owner-grab-button", OwnerGrabButtonMask },
    { 0, 0 }
};
                    
static long
xlib_window_parse_attributes (XSetWindowAttributes *attributes, repv attrs)
{
    long attributesMask = 0;

    while (rep_CONSP (attrs)) {
        repv tem = rep_CAR (attrs);

        if (rep_CONSP (tem)) {
            repv car = rep_CAR (tem);
            if ((car == Qbackground) && COLORP(rep_CDR(tem))) {
                attributes->background_pixel = VCOLOR(rep_CDR(tem))->pixel;
                attributesMask |= CWBackPixel;
            } else if ((car == Qborder_color) && COLORP(rep_CDR(tem))) {
                attributes->border_pixel = VCOLOR(rep_CDR(tem))->pixel;
                attributesMask |= CWBorderPixel;
            } else if (car == Qoverride_redirect) {
                attributes->override_redirect = rep_NILP(rep_CDR(tem)) ? False : True;
                attributesMask |= CWOverrideRedirect;
            } else if (car == Qsave_under) {
                attributes->save_under = rep_NILP(rep_CDR(tem)) ? False : True;
                attributesMask |= CWSaveUnder;
            } else if ((car == Qevent_mask) && rep_LISTP(rep_CDR(tem))) {
                repv evl = rep_CDR (tem);
                attributes->event_mask = 0;
                while (rep_CONSP (evl)) {
                    int mask = x_symbol_match (rep_CAR (evl), x_event_mask_matches);
                    if (mask != -1)
                        attributes->event_mask |= mask;
                    evl = rep_CDR (evl);
                }
                attributesMask |= CWEventMask;
            }
        }

        attrs = rep_CDR (attrs);
    }

    return attributesMask;
}

/* inefficient */
static x_value_str x_event_type_matches[] = {
    { KeyPress, "key-press" },
    { KeyRelease, "key-release" },
    { ButtonPress, "button-press" },
    { ButtonRelease, "button-release" },
    { MotionNotify, "motion-notify" },
    { EnterNotify, "enter-notify" },
    { LeaveNotify, "leave-notify" },
    { FocusIn, "focus-in" },
    { FocusOut, "focus-out" },
    { KeymapNotify, "keymap-notify" },
    { Expose, "expose" },
    { GraphicsExpose, "graphics-expose" },
    { NoExpose, "no-expose" },
    { VisibilityNotify, "visibility-notify" },
    { CreateNotify, "create-notify" },
    { DestroyNotify, "destroy-notify" },
    { UnmapNotify, "unmap-notify" },
    { MapNotify, "map-notify" },
    { MapRequest, "map-request" },
    { ReparentNotify, "reparent-notify" },
    { ConfigureNotify, "configure-notify" },
    { ConfigureRequest, "configure-request" },
    { GravityNotify, "gravity-notify" },
    { ResizeRequest, "resize-request" },
    { CirculateNotify, "circulate-notify" },
    { CirculateRequest, "circulate-request" },
    { PropertyNotify, "property-notify" },
    { SelectionClear, "selection-clear" },
    { SelectionRequest, "selection-request" },
    { SelectionNotify, "selection-notify" },
    { ColormapNotify, "colormap-notify" },
    { ClientMessage, "client-message" },
    { MappingNotify, "mapping-notify" },
    { 0, 0 }
};

static x_value_str x_crossing_mode_matches[] = {
    { NotifyNormal, "notify-normal" },
    { NotifyGrab, "notify-grab" },
    { NotifyUngrab, "notify-ungrab" },
    { 0, 0 }
};

static x_value_str x_crossing_detail_matches[] = {
    { NotifyAncestor, "notify-ancestor" },
    { NotifyVirtual, "notify-virtual" },
    { NotifyInferior, "notify-inferior" },
    { NotifyNonlinear, "notify-nonlinear" },
    { NotifyNonlinearVirtual, "notify-nonlinear-virtual" },
    { 0, 0 }
};

static x_value_str x_motion_is_hint_matches[] = {
    { NotifyNormal, "notify-normal" },
    { NotifyHint, "notify-hint" },
    { 0, 0 }
};

static x_value_str x_button_matches[] = {
    { Button1, "button-1" },
    { Button2, "button-2" },
    { Button3, "button-3" },
    { Button4, "button-4" },
    { Button5, "button-5" },
    { 0, 0 }
};

static x_value_str x_state_matches[] = {
    { Button1Mask, "button-1" },
    { Button2Mask, "button-2" },
    { Button3Mask, "button-3" },
    { Button4Mask, "button-4" },
    { Button5Mask, "button-5" },
    { ShiftMask, "shift" },
    { LockMask, "lock" },
    { ControlMask, "control" },
    { Mod1Mask, "mod-1" },
    { Mod2Mask, "mod-2" },
    { Mod3Mask, "mod-3" },
    { Mod4Mask, "mod-4" },
    { Mod5Mask, "mod-5" },
    { 0, 0 }
};

static repv
x_encode_keysym (unsigned int keycode, unsigned int state) {
   KeySym sym = NoSymbol;
   char *name;
   if (state & ShiftMask)
       sym = XKeycodeToKeysym (dpy, keycode, 1);
   if (sym == NoSymbol)
       sym = XKeycodeToKeysym (dpy, keycode, 0);
   /* I don't reset the shift modifier!!! */
   name = XKeysymToString (sym);
   return name ? Fintern (rep_string_dup (name), Qnil) : Qnil;
}

static repv
x_encode_event (XEvent *ev)
{
    repv event = Qnil, data = Qnil;

    ALIST_PRE (event, Qserial, rep_make_long_uint (ev->xany.serial));
    ALIST_PRE (event, Qsend_event, ev->xany.send_event ? Qt : Qnil);
    ALIST_PRE (event, Qwindow, xlib_window_from_id (ev->xany.window));

    switch (ev->type) {
        case KeyPress:
        case KeyRelease:
            ALIST_PRE (event, Qroot, xlib_window_from_id (ev->xkey.root));
            ALIST_PRE (event, Qsubwindow, xlib_window_from_id (ev->xkey.subwindow));
            ALIST_PRE (event, Qtime, rep_make_long_uint (ev->xkey.time));
            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xkey.x));
            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xkey.y));
            ALIST_PRE (event, Qx_root, rep_MAKE_INT (ev->xkey.x_root));
            ALIST_PRE (event, Qy_root, rep_MAKE_INT (ev->xkey.y_root));
            ALIST_PRE (event, Qstate, x_valuemask_match (ev->xkey.state, x_state_matches));
            ALIST_PRE (event, Qkeycode, x_encode_keysym (ev->xkey.keycode, ev->xkey.state));
            ALIST_PRE (event, Qsame_screen, ev->xkey.same_screen ? Qt : Qnil);
            break;
          
        case ButtonPress:
        case ButtonRelease:
            ALIST_PRE (event, Qroot, xlib_window_from_id (ev->xbutton.root));
            ALIST_PRE (event, Qsubwindow, xlib_window_from_id (ev->xbutton.subwindow));
            ALIST_PRE (event, Qtime, rep_make_long_uint (ev->xbutton.time));
            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xbutton.x));
            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xbutton.y));
            ALIST_PRE (event, Qx_root, rep_MAKE_INT (ev->xbutton.x_root));
            ALIST_PRE (event, Qy_root, rep_MAKE_INT (ev->xbutton.y_root));
            ALIST_PRE (event, Qstate, x_valuemask_match (ev->xbutton.state, x_state_matches));
            ALIST_PRE (event, Qbutton, x_value_match (ev->xbutton.button, x_button_matches));
            ALIST_PRE (event, Qsame_screen, ev->xbutton.same_screen ? Qt : Qnil);
            break;
            
        case MotionNotify:
            ALIST_PRE (event, Qroot, xlib_window_from_id (ev->xmotion.root));
            ALIST_PRE (event, Qsubwindow, xlib_window_from_id (ev->xmotion.subwindow));
            ALIST_PRE (event, Qtime, rep_make_long_uint (ev->xmotion.time));
            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xmotion.x));
            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xmotion.y));
            ALIST_PRE (event, Qx_root, rep_MAKE_INT (ev->xmotion.x_root));
            ALIST_PRE (event, Qy_root, rep_MAKE_INT (ev->xmotion.y_root));
            ALIST_PRE (event, Qstate, x_valuemask_match (ev->xmotion.state, x_state_matches));
            ALIST_PRE (event, Qis_hint, x_value_match (ev->xmotion.is_hint, x_motion_is_hint_matches));
            ALIST_PRE (event, Qsame_screen, ev->xmotion.same_screen ? Qt : Qnil);
            break;
            
        case EnterNotify:
        case LeaveNotify:
            ALIST_PRE (event, Qroot, xlib_window_from_id (ev->xcrossing.root));
            ALIST_PRE (event, Qsubwindow, xlib_window_from_id (ev->xcrossing.subwindow));
            ALIST_PRE (event, Qtime, rep_make_long_uint (ev->xcrossing.time));
            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xcrossing.x));
            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xcrossing.y));
            ALIST_PRE (event, Qx_root, rep_MAKE_INT (ev->xcrossing.x_root));
            ALIST_PRE (event, Qy_root, rep_MAKE_INT (ev->xcrossing.y_root));
            ALIST_PRE (event, Qmode, x_value_match (ev->xcrossing.mode, x_crossing_mode_matches));
            ALIST_PRE (event, Qdetail, x_value_match (ev->xcrossing.detail, x_crossing_detail_matches));
            ALIST_PRE (event, Qsame_screen, ev->xcrossing.same_screen ? Qt : Qnil);
            ALIST_PRE (event, Qfocus, ev->xcrossing.focus ? Qt : Qnil);
            ALIST_PRE (event, Qstate, x_valuemask_match (ev->xcrossing.state, x_state_matches));
            break;
            
        case Expose:
            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xexpose.x));
            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xexpose.y));
            ALIST_PRE (event, Qwidth, rep_MAKE_INT (ev->xexpose.width));
            ALIST_PRE (event, Qheight, rep_MAKE_INT (ev->xexpose.height));
            ALIST_PRE (event, Qcount, rep_MAKE_INT (ev->xexpose.count));
            break;
            
        case ClientMessage:
            ALIST_PRE (event, Qmessage_type, x_atom_symbol (ev->xclient.message_type));
            ALIST_PRE (event, Qformat, rep_MAKE_INT (ev->xclient.format));
            data = Qnil;
            switch (ev->xclient.format) {
                int i;
              
                case 8: /* not a string because length unknown */
                data = Fmake_vector (rep_MAKE_INT (20), Qnil);
                for (i = 0; i < 20; ++ i)
                    rep_VECTI (data, i) = rep_MAKE_INT (ev->xclient.data.b[i]);
                break;
                
                case 16:
                data = Fmake_vector (rep_MAKE_INT (10), Qnil);
                for (i = 0; i < 10; ++ i)
                    rep_VECTI (data, i) = rep_MAKE_INT (ev->xclient.data.s[i]);
                break;
                
                case 32:
                data = Fmake_vector (rep_MAKE_INT (5), Qnil);
                for (i = 0; i < 5; ++ i) /* decoding atoms makes little sense */
                    rep_VECTI (data, i) = rep_MAKE_INT (ev->xclient.data.l[i]);
                break;
            }
            ALIST_PRE (event, Qdata, data);
            break;
    }
          
    /*
    not done...
    FocusIn FocusOut KeymapNotify GraphicsExpose NoExpose VisibilityNotify
    CreateNotify DestroyNotify UnmapNotify MapNotify MapRequest ReparentNotify
    ConfigureNotify ConfigureRequest GravityNotify ResizeRequest CirculateNotify
    CirculateRequest PropertyNotify SelectionClear SelectionRequest
    SelectionNotify ColormapNotify MappingNotify
    */
  
    return event;
}

static void
xlib_window_event_handler (XEvent *ev)
{
    repv win = xlib_window_from_id (ev->xany.window);
    
    if (win != Qnil && VXLIB_WINDOW (win)->event_handler != Qnil)
    {
        repv type = x_value_match (ev->type, x_event_type_matches);
        repv event = x_encode_event (ev);
        repv args = Fcons (type, Fcons (event, Qnil));
        
        rep_funcall (VXLIB_WINDOW(win)->event_handler, args, rep_FALSE);
    }
    
    if (ev->type < LASTEvent && event_handlers[ev->type] != 0)
        event_handlers[ev->type] (ev);
}

static repv
xlib_create_window (Window id, repv ev) {
    Lisp_XLib_Window *w;

    w = rep_ALLOC_CELL(sizeof(Lisp_XLib_Window));
    rep_data_after_gc += sizeof (Lisp_XLib_Window);
    w->car = xlib_window_type;
    w->next = xlib_window_list;
    xlib_window_list = w;
    w->id = id;
    w->back_buffer = 0;
    w->event_handler = ev;
    w->plist = Qnil;

    register_event_handler (id, xlib_window_event_handler);
    XSaveContext (dpy, id, xlib_window_context, (XPointer) w);

    return rep_VAL (w);
}

DEFUN("xlib-create-window", Fxlib_create_window, Sxlib_create_window, (repv xy, repv wh, repv bw, repv attrs, repv ev), rep_Subr5) /*
::doc:xlib-create-window::
xlib-create-window (X . Y) (W . H) BW ATTRS [EVENT-HANDLER]

Creates a new X-WINDOW with the specified position, dimensions and
border width. ATTRS should be a list of cons cells mapping attributes
to values. Known attributes are `background', `border-color',
`override-redirect', `save-under' and `parent'. The window is created
unmapped. If unspecified, override-redirect defaults to t.
::end:: */
{
    repv parent = Qnil;
    Window id, _parent;
    XSetWindowAttributes attributes;
    long attributesMask;
    int _x, _y, _w, _h, _bw;

    rep_DECLARE(1, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE(2, wh, rep_CONSP (wh)
		&& rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));
    rep_DECLARE3(bw, rep_INTP);
    rep_DECLARE4(attrs, rep_LISTP);

    if (rep_CONSP (attrs) && (Fassq (Qparent, attrs) != Qnil))
      parent = rep_CDR (Fassq (Qparent, attrs));
    if (!(_parent = window_from_arg (parent)))
      _parent = root_window;

    _x = rep_INT (rep_CAR (xy));
    _y = rep_INT (rep_CDR (xy));
    _w = rep_INT (rep_CAR (wh));
    _h = rep_INT (rep_CDR (wh));
    _bw = rep_INT (bw);

    /* default to override-redirect for safety; but it can be explicitly overridden */
    attributes.colormap = screen_cmap;
    attributes.override_redirect = True;
    attributesMask = CWColormap | CWOverrideRedirect;
    attributesMask |= xlib_window_parse_attributes (&attributes, attrs);

    id = XCreateWindow (dpy, _parent, _x, _y, _w, _h, _bw,
                        screen_depth, InputOutput, screen_visual,
                        attributesMask, &attributes);

    return xlib_create_window (id, ev);
}

DEFUN("xlib-map-request", Fxlib_map_request, Sxlib_map_request, (repv win), rep_Subr1) /*
::doc:xlib-map-request::
xlib-map-request X-WINDOW
::end:: */
{
    XEvent fake = { MapRequest }; /* ouch the pain */
    rep_DECLARE1(win, XLIB_WINDOWP);
    
    fake.xmaprequest.window = VXLIB_WINDOW(win)->id;

    event_handlers[MapRequest] (&fake);
    
    return Qt;
}

DEFUN("xlib-map-window", Fxlib_map_window, Sxlib_map_window, (repv win), rep_Subr1) /*
::doc:xlib-map-window::
xlib-map-window X-WINDOW
::end:: */
{
    rep_DECLARE1(win, XLIB_WINDOWP);
    XMapWindow (dpy, VXLIB_WINDOW(win)->id);
    return Qt;
}

DEFUN("xlib-map-raised", Fxlib_map_raised, Sxlib_map_raised, (repv win), rep_Subr1) /*
::doc:xlib-map-raised::
xlib-map-raised X-WINDOW
::end:: */
{
    rep_DECLARE1(win, XLIB_WINDOWP);
    XMapRaised (dpy, VXLIB_WINDOW(win)->id);
    return Qt;
}

DEFUN("xlib-map-subwindows", Fxlib_map_subwindows, Sxlib_map_subwindows, (repv win), rep_Subr1) /*
::doc:xlib-map-subwindows::
xlib-map-subwindows X-WINDOW
::end:: */
{
    rep_DECLARE1(win, XLIB_WINDOWP);
    XMapSubwindows (dpy, VXLIB_WINDOW(win)->id);
    return Qt;
}

DEFUN("xlib-unmap-window", Fxlib_unmap_window, Sxlib_unmap_window, (repv win), rep_Subr1) /*
::doc:xlib-unmap-window::
xlib-unmap-window X-WINDOW
::end:: */
{
    rep_DECLARE1(win, XLIB_WINDOWP);
    XUnmapWindow (dpy, VXLIB_WINDOW(win)->id);
    return Qt;
}

DEFUN("xlib-unmap-subwindows", Fxlib_unmap_subwindows, Sxlib_unmap_subwindows, (repv win), rep_Subr1) /*
::doc:xlib-unmap-subwindows::
xlib-unmap-subwindows X-WINDOW
::end:: */
{
    rep_DECLARE1(win, XLIB_WINDOWP);
    XUnmapSubwindows (dpy, VXLIB_WINDOW(win)->id);
    return Qt;
}

DEFUN("xlib-configure-window", Fxlib_configure_window, Sxlib_configure_window, (repv window, repv attrs), rep_Subr2) /*
::doc:xlib-configure-window::
xlib-configure-window WINDOW ATTRS

Reconfigures the X-WINDOW. ATTRS should be an alist mapping attribute
names to values. Known attributes include the symbols `x', `y',
`width', `height', `border-width', `sibling' and `stack-mode'. Valid
values for stack-mode are `above', `below', `top-if', `bottom-if' and
`opposite'.
::end:: */
{
    XWindowChanges changes;
    long changesMask;

    rep_DECLARE1(window, XLIB_WINDOWP);
    rep_DECLARE2(attrs, rep_LISTP);

    changesMask = xlib_window_parse_changes (&changes, attrs);

    if (changesMask)
      XConfigureWindow (dpy, VXLIB_WINDOW(window)->id, changesMask, &changes);

    return Qt;
}

DEFUN("xlib-configure-request", Fxlib_configure_request, Sxlib_configure_request, (repv window, repv attrs), rep_Subr2) /*
::doc:xlib-configure-request::
xlib-configure-request WINDOW ATTRS
::end:: */
{
    XWindowChanges changes;
    long changesMask;

    rep_DECLARE1(window, XLIB_WINDOWP);
    rep_DECLARE2(attrs, rep_LISTP);

    changesMask = xlib_window_parse_changes (&changes, attrs);

    if (changesMask) {
      XEvent fake = { ConfigureRequest };
      
      fake.xconfigurerequest.display = dpy;
      fake.xconfigurerequest.window = VXLIB_WINDOW(window)->id;
      fake.xconfigurerequest.x = changes.x;
      fake.xconfigurerequest.y = changes.y;
      fake.xconfigurerequest.width = changes.width;
      fake.xconfigurerequest.height = changes.height;
      fake.xconfigurerequest.border_width = changes.border_width;
      fake.xconfigurerequest.above = changes.sibling;
      fake.xconfigurerequest.detail = changes.stack_mode;
      fake.xconfigurerequest.value_mask = changesMask;
      
      event_handlers[ConfigureRequest] (&fake);
    }

    return Qt;
}

DEFUN("xlib-change-window-attributes", Fxlib_change_window_attributes, Sxlib_change_window_attributes, (repv window, repv attrs), rep_Subr2) /*
::doc:xlib-change-window-attributes::
xlib-change-window-attributes WINDOW ATTRS

Sets attributes of the X-WINDOW. ATTRS should be an alist mapping
attribute names to values. Known attributes include the symbols
`background', `border-color', `override-redirect' and `save-under'.
::end:: */
{
    XSetWindowAttributes attributes;
    long attributesMask;

    rep_DECLARE1(window, XLIB_WINDOWP);
    rep_DECLARE2(attrs, rep_LISTP);

    attributesMask = xlib_window_parse_attributes (&attributes, attrs);

    if (attributesMask)
      XChangeWindowAttributes (dpy, VXLIB_WINDOW(window)->id, attributesMask, &attributes);

    return Qt;
}

DEFUN("xlib-raise-window", Fxlib_raise_window, Sxlib_raise_window, (repv window), rep_Subr1) /*
::doc:xlib-raise-window::
xlib-raise-window WINDOW

Raises the X-WINDOW.
::end:: */
{
    rep_DECLARE1(window, XLIB_WINDOWP);

    XRaiseWindow (dpy, VXLIB_WINDOW(window)->id);

    return Qt;
}

DEFUN("xlib-lower-window", Fxlib_lower_window, Sxlib_lower_window, (repv window), rep_Subr1) /*
::doc:xlib-lower-window::
xlib-lower-window WINDOW

Lowers the X-WINDOW.
::end:: */
{
    rep_DECLARE1(window, XLIB_WINDOWP);

    XLowerWindow (dpy, VXLIB_WINDOW(window)->id);

    return Qt;
}

DEFUN("xlib-circulate-subwindows", Fxlib_circulate_subwindows, Sxlib_circulate_subwindows, (repv window, repv direction), rep_Subr2) /*
::doc:xlib-circulate-subwindows::
xlib-circulate-subwindows WINDOW DIRECTION

Circulates the subwindows of the X-WINDOW in DIRECTION
for either `raise-lowest' or `lower-highest'.
::end:: */
{
    int _direction;
  
    rep_DECLARE1(window, XLIB_WINDOWP);
    rep_DECLARE(2, direction, (direction == Qraise_lowest) || (direction == Qlower_highest));
    _direction = (direction == Qraise_lowest) ? RaiseLowest : LowerHighest;

    XCirculateSubwindows (dpy, VXLIB_WINDOW(window)->id, _direction);

    return Qt;
}

DEFUN("xlib-circulate-subwindows-up", Fxlib_circulate_subwindows_up, Sxlib_circulate_subwindows_up, (repv window), rep_Subr1) /*
::doc:xlib-circulate-subwindows-up::
xlib-circulate-subwindows-up WINDOW

Circulates up the subwindows of the X-WINDOW.
::end:: */
{
    rep_DECLARE1(window, XLIB_WINDOWP);

    XCirculateSubwindowsUp (dpy, VXLIB_WINDOW(window)->id);

    return Qt;
}

DEFUN("xlib-circulate-subwindows-down", Fxlib_circulate_subwindows_down, Sxlib_circulate_subwindows_down, (repv window), rep_Subr1) /*
::doc:xlib-circulate-subwindows-down::
xlib-circulate-subwindows-down WINDOW

Circulates down the subwindows of the X-WINDOW.
::end:: */
{
    rep_DECLARE1(window, XLIB_WINDOWP);

    XCirculateSubwindowsDown (dpy, VXLIB_WINDOW(window)->id);

    return Qt;
}

DEFUN("xlib-restack-windows", Fxlib_restack_windows, Sxlib_restack_windows, (repv list), rep_Subr1) /*
::doc:xlib-restack-windows::
xlib-restack-windows LIST

Restacks the LIST of X-WINDOWs.
::end:: */
{
    Window *windows;
    int n = 0;
    
    rep_DECLARE1(list, rep_LISTP);

    windows = alloca (rep_INT (Flength (list)) * sizeof (Window));
    while (rep_CONSP (list)) {
      if (XLIB_WINDOWP (rep_CAR (list)))
        windows[n ++] = VXLIB_WINDOW (rep_CAR (list))->id;
      list = rep_CDR (list);
    }
    XRestackWindows (dpy, windows, n);

    return Qt;
}

DEFUN("xlib-destroy-window", Fxlib_destroy_window, Sxlib_destroy_window, (repv window), rep_Subr1) /*
::doc:xlib-destroy-window::
xlib-destroy-window WINDOW

Destroys the X-WINDOW.
::end:: */
{
    rep_DECLARE1(window, XLIB_WINDOWP);

    XDeleteContext (dpy, VXLIB_WINDOW(window)->id, xlib_window_context);
    deregister_event_handler (VXLIB_WINDOW(window)->id); 
    XDestroyWindow (dpy, VXLIB_WINDOW(window)->id);
    VXLIB_WINDOW(window)->id = 0;

    return Qt;
}

DEFUN("xlib-window-id", Fxlib_window_id, Sxlib_window_id, (repv window), rep_Subr1) /*
::doc:xlib-window-id::
xlib-window-id WINDOW

Return the X11 window-id (an integer) associated with X-WINDOW.
::end:: */
{
    rep_DECLARE1(window, XLIB_WINDOWP);

    return rep_MAKE_INT (VXLIB_WINDOW(window)->id);
}

DEFUN("xlib-window-put", Fxlib_window_put, Sxlib_window_put, (repv window, repv key, repv value), rep_Subr3) /*
::doc:xlib-window-put::
xlib-window-put WINDOW KEY VALUE

Stores the specified VALUE in the specified WINDOW under the specified
(symbolic) KEY.
::end:: */
{
    repv plist, ptr;
  
    rep_DECLARE1(window, XLIB_WINDOWP);
    rep_DECLARE2(key, rep_SYMBOLP);

    ptr = plist = VXLIB_WINDOW(window)->plist;
    while (ptr != Qnil) {
      repv cons = rep_CAR (ptr);
      if (rep_CAR (cons) == key) {
          rep_CDR (cons) = value;
          return Qt;
      }
      ptr = rep_CDR (ptr);
    }
    VXLIB_WINDOW(window)->plist = Fcons (Fcons (key, value), plist);
    
    return Qt;
}

DEFUN("xlib-window-get", Fxlib_window_get, Sxlib_window_get, (repv window, repv key), rep_Subr2) /*
::doc:xlib-window-get::
xlib-window-get WINDOW KEY

Gets the value stored in the specified WINDOW under the specified
(symbolic) KEY.
::end:: */
{
    repv plist, ptr;
  
    rep_DECLARE1(window, XLIB_WINDOWP);
    rep_DECLARE2(key, rep_SYMBOLP);

    ptr = plist = VXLIB_WINDOW(window)->plist;
    while (ptr != Qnil) {
      repv cons = rep_CAR (ptr);
      if (rep_CAR (cons) == key)
          return rep_CDR (cons);
      ptr = rep_CDR (ptr);
    }
    
    return Qnil;
}

DEFUN("xlib-window-p", Fxlib_window_p, Sxlib_window_p, (repv window), rep_Subr1) /*
::doc:xlib-window-p::
xlib-window-p ARG

Return t if ARG is a X-WINDOW object.
::end:: */
{
    return XLIB_WINDOWP(window) ? Qt : Qnil;
}

DEFUN("xlib-set-text-property", Fxlib_set_text_property, Sxlib_set_text_property, (repv window, repv textv, repv property), rep_Subr3) /*
::doc:xlib-set-text-property::
xlib-set-text-property X-WINDOW TEXTV PROPERTY

Sets the specified PROPERTY on the specified X-WINDOW to the specified
value TEXTV, a vector of strings.
::end:: */
{
    Atom _prop;
    int i, n;
    char **_textv;
    XTextProperty textprop;
  
    rep_DECLARE1 (window, XLIB_WINDOWP);
    rep_DECLARE2 (textv, rep_VECTORP);
    n = rep_VECT_LEN (textv);
    for (i = 0; i < n; ++ i)
        rep_DECLARE (2, textv, rep_STRINGP (rep_VECTI (textv, i)));
    rep_DECLARE3 (property, rep_SYMBOLP);
    
    _prop = xlib_atom_symbol (property);
    _textv = alloca (n * sizeof (char *));
    for (i = 0; i < n; ++ i)
        _textv[i] = rep_STR (rep_VECTI (textv, i));
    if (!XStringListToTextProperty (_textv, n, &textprop))
        return Qnil;
    
    XSetTextProperty (dpy, VXLIB_WINDOW(window)->id, &textprop, _prop);
    XFree (textprop.value);

    return Qt;
}

DEFUN("xlib-get-text-property", Fxlib_get_text_property, Sxlib_get_text_property, (repv window, repv property), rep_Subr2) /*
::doc:xlib-get-text-property::
xlib-get-text-property X-WINDOW PROPERTY

Gets the specified PROPERTY of the specified X-WINDOW as a vector
of strings.
::end:: */
{
    Atom _prop;
    XTextProperty textprop;
    int i, n;
    char **_textv;
    repv textv;
  
    rep_DECLARE1 (window, XLIB_WINDOWP);
    rep_DECLARE2 (property, rep_SYMBOLP);
    
    _prop = xlib_atom_symbol (property);
    if (!XGetTextProperty (dpy, VXLIB_WINDOW(window)->id, &textprop, _prop))
        return Qnil;
    if (!XTextPropertyToStringList (&textprop, &_textv, &n)) {
        XFree (textprop.value);
        return Qnil;
    }
    XFree (textprop.value);
    textv = Fmake_vector (rep_MAKE_INT (n), Qnil);
    for (i = 0; i < n; ++ i)
        rep_VECTI (textv, i) = rep_string_dup (_textv[i]);
    XFreeStringList (_textv);

    return textv;
}

DEFUN("xlib-list-properties", Fxlib_list_properties, Sxlib_list_properties, (repv window), rep_Subr1) /*
::doc:xlib-list-properties::
xlib-list-properties X-WINDOW

Returns a list of the properties of the specified X-WINDOW.
::end:: */
{
    Atom *atoms;
    char **_props;
    repv props = Qnil;
    int i, n;
  
    rep_DECLARE1 (window, XLIB_WINDOWP);

    atoms = XListProperties (dpy, VXLIB_WINDOW(window)->id, &n);
    if (!atoms)
      return Qnil;
    _props = alloca (n * sizeof (char *));
    if (!XGetAtomNames (dpy, atoms, n, _props)) {
      XFree (atoms);
      return Qnil;
    }
    XFree (atoms);
    for (i = n - 1; i >= 0; -- i)
      props = Fcons (Fintern (rep_string_dup (_props[i]), Qnil), props);
    for (i = 0; i < n; ++ i)
      XFree (_props[i]);

    return props;
}

static x_str_value x_change_property_mode_matches[] = {
    { "prop-mode-replace", PropModeReplace },
    { "prop-mode-prepend", PropModePrepend },
    { "prop-mode-append", PropModeAppend },
    { 0, 0 }
};

DEFUN("xlib-change-property", Fxlib_change_property, Sxlib_change_property, (repv args), rep_SubrN) /*
::doc:xlib-change-property::
xlib-change-property X-WINDOW PROPERTY TYPE FORMAT MODE DATAV

Sets the specified PROPERTY in the specified X-WINDOW to the
specified TYPE vector value DATAV in format FORMAT. MODE can be
`prop-mode-replace', `prop-mode-prepend' or `prop-mode-append'.
::end:: */
{
    repv window, property, type, format, mode, datav;
    Window _window;
    Atom _property, _type;
    int _format, _mode;
    void *_data;
    int i, n;

    nDECLARE (1, window, XLIB_WINDOWP (window));
    _window = VXLIB_WINDOW(window)->id;
    nDECLARE (2, property, rep_SYMBOLP (property));
    _property = xlib_atom_symbol (property);
    nDECLARE (3, type, rep_SYMBOLP (type));
    _type = xlib_atom_symbol (type);
    nDECLARE (4, format, rep_INTP (format));
    _format = rep_INT (format);
    rep_DECLARE (4, format, (_format == 8) || (_format == 16) || (_format == 32));;
    nDECLARE (5, mode, rep_SYMBOLP (mode));
    _mode = x_symbol_match (mode, x_change_property_mode_matches);
    rep_DECLARE (5, mode, (_mode != -1));
    nDECLARE (6, datav, rep_VECTORP (datav));
    n = rep_VECT_LEN (datav);
    for (i = 0; i < n; ++ i)
        rep_DECLARE (6, datav, rep_INTP (rep_VECTI (datav, i)));
    
    _data = alloca (n * 4);
    for (i = 0; i < n; ++ i) {
      int datum = rep_INT (rep_VECTI (datav, i));
      if (format == 8)
        ((char *) _data)[i] = (char) datum;
      else if (format == 16)
        ((short *) _data)[i] = (short) datum;
      else
        ((int *) _data)[i] = datum;
    }
    XChangeProperty (dpy, _window, _property, _type, _format, _mode, _data, n);

    return Qt;
}

DEFUN("xlib-rotate-window-properties", Fxlib_rotate_window_properties, Sxlib_rotate_window_properties, (repv window, repv list, repv npos), rep_Subr3) /*
::doc:xlib-rotate-window-properties::
xlib-rotate-window-properties X-WINDOW PROPERTIES NPOS

Rotates the values of the specified list of X-WINDOW PROPERTIES by NPOS.
::end:: */
{
    Atom *atoms;
    int n = 0;
    int _npos;

    rep_DECLARE1 (window, XLIB_WINDOWP);
    rep_DECLARE2 (list, rep_LISTP);
    rep_DECLARE3 (npos, rep_INTP);

    _npos = rep_INT (npos);

    atoms = alloca (rep_INT (Flength (list)) * sizeof (Atom));
    while (rep_CONSP (list)) {
      if (rep_SYMBOLP (rep_CAR (list)))
        atoms[n ++] = xlib_atom_symbol (rep_CAR (list));
      list = rep_CDR (list);
    }
    XRotateWindowProperties (dpy, VXLIB_WINDOW(window)->id, atoms, n, _npos);

    return Qt;
}

DEFUN("xlib-delete-property", Fxlib_delete_property, Sxlib_delete_property, (repv window, repv property), rep_Subr2) /*
::doc:xlib-delete-property::
xlib-delete-property X-WINDOW PROPERTY

Deletes the specified PROPERTY from the specified X-WINDOW.
::end:: */
{
    Atom _prop;
  
    rep_DECLARE1 (window, XLIB_WINDOWP);
    rep_DECLARE2 (property, rep_SYMBOLP);
    
    _prop = xlib_atom_symbol (property);
    XDeleteProperty (dpy, VXLIB_WINDOW(window)->id, _prop);

    return Qt;
}

DEFUN("xlib-window-back-buffer", Fxlib_window_back_buffer,
      Sxlib_window_back_buffer, (repv win), rep_Subr1)
{
    rep_DECLARE1(win, XLIB_WINDOWP);

    if (VXLIB_WINDOW(win)->back_buffer == 0 && have_dbe)
    {
	VXLIB_WINDOW(win)->back_buffer = (XdbeAllocateBackBufferName
				       (dpy, VXLIB_WINDOW(win)->id,
					XdbeBackground));
    }

    if (VXLIB_WINDOW(win)->back_buffer == 0)
	return Qnil;
    else
	return rep_MAKE_INT (VXLIB_WINDOW(win)->back_buffer);
}

DEFUN("xlib-window-swap-buffers", Fxlib_window_swap_buffers,
      Sxlib_window_swap_buffers, (repv win), rep_Subr1)
{
    rep_DECLARE1(win, XLIB_WINDOWP);
    if (VXLIB_WINDOW(win)->back_buffer != 0)
    {
	XdbeSwapInfo info;
	info.swap_window = VXLIB_WINDOW(win)->id;
	info.swap_action = XdbeBackground;
	XdbeSwapBuffers (dpy, &info, 1);
    }
    return Qt;
}


/* Drawing functions */

DEFUN("xlib-clear-window", Fxlib_clear_window, Sxlib_clear_window, (repv window), rep_Subr1) /*
::doc:xlib-clear-window::
xlib-clear-window WINDOW

Clears the window associated with WINDOW to its background color.
::end:: */
{
    Window id = window_from_arg (window);
    rep_DECLARE(1, window, id != 0);

    XClearWindow (dpy, id);
    return Qt;
}

DEFUN("xlib-draw-string", Fxlib_draw_string, Sxlib_draw_string, (repv window, repv gc, repv xy, repv string, repv font), rep_Subr5) /*
::doc:xlib-draw-string::
xlib-draw-string WINDOW GC (X . Y) STRING [FONT]

Draws the specified string at the specified location in the optional
specified font in the window associated with WINDOW.
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0;
    int y = 0;
    unsigned char *str;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE4(string, rep_STRINGP);
    if (font == Qnil)
	font = Fsymbol_value (Qdefault_font, Qt);
    rep_DECLARE5(font, FONTP);

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    str = rep_STR (string);

    x_draw_string (id, font, VXLIB_GC(gc)->gc, x, y, str, strlen (str));
    return Qt;
}

DEFUN("xlib-draw-line", Fxlib_draw_line, Sxlib_draw_line, (repv window, repv gc, repv start, repv end), rep_Subr4) /*
::doc:xlib-draw-line::
xlib-draw-line WINDOW GC (X1 . Y1) (X2 . Y2)

Draws a line from (X1, Y1) to (X2, Y2) in WINDOW, using GC.
::end:: */
{
    Window id = window_from_arg (window);
    int x1 = 0, y1 = 0;
    int x2 = 0, y2 = 0;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, start, rep_CONSP (start)
		&& rep_INTP (rep_CAR (start)) && rep_INTP (rep_CDR (start)));
    rep_DECLARE(4, end, rep_CONSP (end)
		&& rep_INTP (rep_CAR (end)) && rep_INTP (rep_CDR (end)));

    x1 = rep_INT (rep_CAR (start));
    y1 = rep_INT (rep_CDR (start));
    x2 = rep_INT (rep_CAR (end));
    y2 = rep_INT (rep_CDR (end));

    XDrawLine (dpy, id, VXLIB_GC(gc)->gc, x1, y1, x2, y2);
    return Qt;
}

DEFUN("xlib-draw-rectangle", Fxlib_draw_rectangle, Sxlib_draw_rectangle, (repv window, repv gc, repv xy, repv wh), rep_Subr4) /*
::doc:xlib-draw-rectangle::
xlib-draw-rectangle WINDOW GC (X . Y) (WIDTH . HEIGHT)

Draws a rectangle with top-left corner (X1, Y1) and dimensions (WIDTH,
HEIGHT) in WINDOW, using GC.
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0, y = 0;
    int w = 0, h = 0;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE(4, wh, rep_CONSP (wh)
		&& rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    w = rep_INT (rep_CAR (wh));
    h = rep_INT (rep_CDR (wh));

    XDrawRectangle (dpy, id, VXLIB_GC(gc)->gc, x, y, w, h);
    return Qt;
}

DEFUN("xlib-fill-rectangle", Fxlib_fill_rectangle, Sxlib_fill_rectangle, (repv window, repv gc, repv xy, repv wh), rep_Subr4) /*
::doc:xlib-fill-rectangle::
xlib-fill-rectangle WINDOW GC (X . Y) (WIDTH . HEIGHT)

Draws a filled rectangle with top-left corner (X, Y) and dimensions
(WIDTH, HEIGHT) in WINDOW, using GC.
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0, y = 0;
    int w = 0, h = 0;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE(4, wh, rep_CONSP (wh)
		&& rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    w = rep_INT (rep_CAR (wh));
    h = rep_INT (rep_CDR (wh));

    XFillRectangle (dpy, id, VXLIB_GC(gc)->gc, x, y, w, h);
    return Qt;
}

DEFUN("xlib-draw-arc", Fxlib_draw_arc, Sxlib_draw_arc, (repv window, repv gc, repv xy, repv wh, repv angle), rep_Subr5) /*
::doc:xlib-draw-arc::
xlib-draw-arc WINDOW GC (X . Y) (WIDTH . HEIGHT) (ANGLE1 . ANGLE2)

Draws a single circular or elliptical arc. Each arc is specified by a
rectangle and two angles.

The center of the circle or ellipse is the center of the rectangle, and
the major and minor axes are specified by the width and height. 
Positive angles indicate counter-clockwise motion, and negative angles
indicate clockwise motion.

(See XDrawArc (3X11) for more details.)
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0, y = 0;
    int w = 0, h = 0;
    int a1 = 0, a2 = 0;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE(4, wh, rep_CONSP (wh)
		&& rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));
    rep_DECLARE(5, angle, rep_CONSP (angle)
		&& rep_INTP (rep_CAR (angle)) && rep_INTP (rep_CDR (angle)));

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    w = rep_INT (rep_CAR (wh));
    h = rep_INT (rep_CDR (wh));
    a1 = rep_INT (rep_CAR (angle));
    a2 = rep_INT (rep_CDR (angle));

    XDrawArc (dpy, id, VXLIB_GC(gc)->gc, x, y, w, h, a1, a2);
    return Qt;
}

DEFUN("xlib-fill-arc", Fxlib_fill_arc, Sxlib_fill_arc, (repv window, repv gc, repv xy, repv wh, repv angle), rep_Subr5) /*
::doc:xlib-fill-arc::
xlib-fill-arc WINDOW GC (X . Y) (WIDTH . HEIGHT) (ANGLE1 . ANGLE2)

Draws a single filled circular or elliptical arc. Each arc is specified
by a rectangle and two angles.

The center of the circle or ellipse is the center of the rectangle, and
the major and minor axes are specified by the width and height. 
Positive angles indicate counter-clockwise motion, and negative angles
indicate clockwise motion.

(See XFillArc (3X11) for more details.)
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0, y = 0;
    int w = 0, h = 0;
    int a1 = 0, a2 = 0;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE(4, wh, rep_CONSP (wh)
		&& rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));
    rep_DECLARE(5, angle, rep_CONSP (angle)
		&& rep_INTP (rep_CAR (angle)) && rep_INTP (rep_CDR (angle)));

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    w = rep_INT (rep_CAR (wh));
    h = rep_INT (rep_CDR (wh));
    a1 = rep_INT (rep_CAR (angle));
    a2 = rep_INT (rep_CDR (angle));

    XFillArc (dpy, id, VXLIB_GC(gc)->gc, x, y, w, h, a1, a2);
    return Qt;
}

DEFUN("xlib-fill-polygon", Fxlib_fill_polygon, Sxlib_fill_polygon, (repv window, repv gc, repv points, repv mode_), rep_Subr4) /*
::doc:xlib-fill-arc::
xlib-fill-arc WINDOW GC POINTS [MODE]

Draws a single filled polygon in WINDOW using GC. Each point is `(X . Y)'.
::end:: */
{
    Window id = window_from_arg (window);
    repv npoints;
    XPoint *xpoints;
    int i, mode;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, points, rep_LISTP (points));

    if (mode_ == Qconvex)
	mode = Convex;
    else if (mode_ == Qnon_convex)
	mode = Nonconvex;
    else
	mode = Complex;

    npoints = Flength (points);
    if (!npoints)
	return rep_NULL;

    npoints = rep_INT (npoints);
    xpoints = alloca (sizeof (XPoint) * npoints);
    for (i = 0; i < npoints; i++)
    {
	if (!rep_CONSP (points) || !rep_CONSP (rep_CAR (points))
	    || !rep_INTP (rep_CAAR (points)) || !rep_INTP (rep_CDAR (points)))
	    return rep_signal_arg_error (points, 3);
	xpoints[i].x = rep_INT (rep_CAAR (points));
	xpoints[i].y = rep_INT (rep_CDAR (points));
	points = rep_CDR (points);
    }

    XFillPolygon (dpy, id, VXLIB_GC(gc)->gc, xpoints,
		  npoints, mode, CoordModeOrigin);
    return Qt;
}

DEFUN("xlib-copy-area", Fxlib_copy_area, Sxlib_copy_area, (repv window, repv gc, repv xy, repv wh, repv dest), rep_Subr5) /*
::doc:xlib-fill-rectangle::
xlib-fill-rectangle WINDOW GC (X . Y) (WIDTH . HEIGHT) (DEST-X . DEST-Y)

Copy a region of WINDOW with top-left corner (X, Y) and dimensions
(WIDTH, HEIGHT), to the position (DEST-X, DEST-Y), using GC.
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0, y = 0;
    int w = 0, h = 0;
    int dx = 0, dy = 0;

    rep_DECLARE(1, window, id != 0);
    rep_DECLARE(2, gc, XLIB_VALID_GCP (gc, id));
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    rep_DECLARE(4, wh, rep_CONSP (wh)
		&& rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));
    rep_DECLARE(5, dest, rep_CONSP (dest)
		&& rep_INTP (rep_CAR (dest)) && rep_INTP (rep_CDR (dest)));

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    w = rep_INT (rep_CAR (wh));
    h = rep_INT (rep_CDR (wh));
    dx = rep_INT (rep_CAR (dest));
    dy = rep_INT (rep_CDR (dest));

    XCopyArea (dpy, id, id, VXLIB_GC(gc)->gc, x, y, w, h, dx, dy);
    return Qt;
}

DEFUN("xlib-draw-image", Fxlib_draw_image, Sxlib_draw_image, (repv img, repv window, repv xy, repv wh), rep_Subr4) /*
::doc:xlib-draw-image::
xlib-draw-image IMAGE WINDOW (X . Y) [(WIDTH . HEIGHT)]

Render the image object IMAGE in WINDOW at position (X, Y). If WIDTH
and HEIGHT are defined the image is first scaled to these dimensions,
otherwise it is drawn using its natural dimensions.
::end:: */
{
    Window id = window_from_arg (window);
    int x = 0, y = 0;
    int w = 0, h = 0;

    rep_DECLARE1(img, IMAGEP);
    rep_DECLARE(2, window, id != 0);
    rep_DECLARE(3, xy, rep_CONSP (xy)
		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
    if (wh != Qnil)
	rep_DECLARE(4, wh, rep_CONSP (wh)
		    && rep_INTP (rep_CAR (wh)) && rep_INTP (rep_CDR (wh)));

    x = rep_INT (rep_CAR (xy));
    y = rep_INT (rep_CDR (xy));
    w = (wh == Qnil) ? VIMAGE(img)->image->rgb_width : rep_INT (rep_CAR (wh));
    h = (wh == Qnil) ? VIMAGE(img)->image->rgb_height : rep_INT (rep_CDR (wh));

    Imlib_paste_image (imlib_id, VIMAGE(img)->image, id, x, y, w, h);

    /* Imlib sometimes calls XSync (), which could hide events */
    rep_mark_input_pending (ConnectionNumber(dpy));

    return Qt;
}


/* gc type hooks */

static int
xlib_gc_cmp (repv w1, repv w2)
{
    return w1 != w2;
}

static void
xlib_gc_prin (repv stream, repv obj)
{
    char buf[256];
    sprintf (buf, "#<xlib-gc>");
    rep_stream_puts (stream, buf, -1, FALSE);
}

static void
xlib_gc_mark (repv obj)
{
}

static void
xlib_gc_sweep (void)
{
    Lisp_XLib_GC *w = xlib_gc_list;
    xlib_gc_list = 0;
    while (w != 0)
    {
	Lisp_XLib_GC *next = w->next;
	if (!rep_GC_CELL_MARKEDP(rep_VAL(w)))
	{
            if (w->gc != 0)
                Fxlib_free_gc(rep_VAL(w));
	    rep_FREE_CELL(w);
	}
	else
	{
	    rep_GC_CLR_CELL(rep_VAL(w));
	    w->next = xlib_gc_list;
	    xlib_gc_list = w;
	}
	w = next;
    }
}


/* window type hooks */

static int
xlib_window_cmp (repv w1, repv w2)
{
    return w1 != w2;
}

static void
xlib_window_prin (repv stream, repv obj)
{
    char buf[256];
    sprintf (buf, "#<xlib-window 0x%lx>", VXLIB_WINDOW(obj)->id);
    rep_stream_puts (stream, buf, -1, FALSE);
}

static void
xlib_window_mark (repv obj)
{
    rep_MARKVAL (VXLIB_WINDOW (obj)->event_handler);
    rep_MARKVAL (VXLIB_WINDOW (obj)->plist);
}

static void
xlib_window_sweep (void)
{
    Lisp_XLib_Window *w = xlib_window_list;
    xlib_window_list = 0;
    while (w != 0)
    {
	Lisp_XLib_Window *next = w->next;
	if (!rep_GC_CELL_MARKEDP(rep_VAL(w)))
	{
            if (w->id != 0)
                Fxlib_destroy_window(rep_VAL(w));
	    rep_FREE_CELL(w);
	}
	else
	{
	    rep_GC_CLR_CELL(rep_VAL(w));
	    w->next = xlib_window_list;
	    xlib_window_list = w;
	}
	w = next;
    }
}


/* initialisation */

repv
rep_dl_init (void)
{
    xlib_gc_type = rep_register_new_type ("xlib-gc", xlib_gc_cmp, xlib_gc_prin, xlib_gc_prin,
				       xlib_gc_sweep, xlib_gc_mark,
				       0, 0, 0, 0, 0, 0, 0);
    rep_ADD_SUBR(Sxlib_create_gc);
    rep_ADD_SUBR(Sxlib_change_gc);
    rep_ADD_SUBR(Sxlib_free_gc);
    rep_ADD_SUBR(Sxlib_gc_p);

    xlib_window_context = XUniqueContext ();

    xlib_window_type = rep_register_new_type ("xlib-window", xlib_window_cmp,
					   xlib_window_prin, xlib_window_prin,
				           xlib_window_sweep, xlib_window_mark,
				           0, 0, 0, 0, 0, 0, 0);
    rep_ADD_SUBR(Sxlib_create_window);
    rep_ADD_SUBR(Sxlib_map_request);
    rep_ADD_SUBR(Sxlib_map_window);
    rep_ADD_SUBR(Sxlib_map_raised);
    rep_ADD_SUBR(Sxlib_map_subwindows);
    rep_ADD_SUBR(Sxlib_unmap_window);
    rep_ADD_SUBR(Sxlib_unmap_subwindows);
    rep_ADD_SUBR(Sxlib_configure_request);
    rep_ADD_SUBR(Sxlib_configure_window);
    rep_ADD_SUBR(Sxlib_change_window_attributes);
    rep_ADD_SUBR(Sxlib_raise_window);
    rep_ADD_SUBR(Sxlib_lower_window);
    rep_ADD_SUBR(Sxlib_circulate_subwindows);
    rep_ADD_SUBR(Sxlib_circulate_subwindows_up);
    rep_ADD_SUBR(Sxlib_circulate_subwindows_down);
    rep_ADD_SUBR(Sxlib_restack_windows);
    rep_ADD_SUBR(Sxlib_destroy_window);
    rep_ADD_SUBR(Sxlib_window_p);
    rep_ADD_SUBR(Sxlib_window_id);
    rep_ADD_SUBR(Sxlib_window_put);
    rep_ADD_SUBR(Sxlib_window_get);
    rep_ADD_SUBR(Sxlib_window_back_buffer);
    rep_ADD_SUBR(Sxlib_window_swap_buffers);

    rep_ADD_SUBR(Sxlib_list_properties);
    rep_ADD_SUBR(Sxlib_change_property);
    rep_ADD_SUBR(Sxlib_rotate_window_properties);
    rep_ADD_SUBR(Sxlib_delete_property);
    rep_ADD_SUBR(Sxlib_set_text_property);
    rep_ADD_SUBR(Sxlib_get_text_property);
    
    rep_ADD_SUBR(Sxlib_clear_window);
    rep_ADD_SUBR(Sxlib_draw_string);
    rep_ADD_SUBR(Sxlib_draw_line);
    rep_ADD_SUBR(Sxlib_draw_rectangle);
    rep_ADD_SUBR(Sxlib_fill_rectangle);
    rep_ADD_SUBR(Sxlib_draw_arc);
    rep_ADD_SUBR(Sxlib_fill_arc);
    rep_ADD_SUBR(Sxlib_fill_polygon);
    rep_ADD_SUBR(Sxlib_copy_area);
    rep_ADD_SUBR(Sxlib_draw_image);

    rep_INTERN(x);
    rep_INTERN(y);
    rep_INTERN(sibling);
    rep_INTERN(stack_mode);
    
    rep_INTERN(raise_lowest);
    rep_INTERN(lower_highest);
    
    rep_INTERN(border_width);
    rep_INTERN(border_color);
    rep_INTERN(override_redirect);
    rep_INTERN(save_under);
    rep_INTERN(event_mask);
    rep_INTERN(parent);
    
    rep_INTERN(serial);
    rep_INTERN(send_event);
    rep_INTERN(window);
    rep_INTERN(subwindow);
    rep_INTERN(time);
    rep_INTERN(x_root);
    rep_INTERN(y_root);
    rep_INTERN(state);
    rep_INTERN(keycode);
    rep_INTERN(same_screen);
    rep_INTERN(button);
    rep_INTERN(is_hint);
    rep_INTERN(focus);
    rep_INTERN(mode);
    rep_INTERN(detail);
    rep_INTERN(count);
    rep_INTERN(message_type);
    rep_INTERN(format);
    rep_INTERN(data);

    rep_INTERN(convex);
    rep_INTERN(non_convex);

    if (dpy != 0)
    {
	int major, minor;
	if (XdbeQueryExtension (dpy, &major, &minor))
	    have_dbe = TRUE;
    }

    return Qx;
}
