#
# version -0.8.7
#
# Copyright (C) 2000-2002 merlin <merlin@merlin.org>
#
# Built from sawfish 1.2
#
# *********************
# ** 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
# Sawfish.
#
# Sawfish was not written with code of this nature on mind.
#
# More to the point, Sawfish 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 Sawfish itself.
#
# ******************
# ** INSTALLATION **
# ******************
#
# I assume that you have a recent copy of the Sawfish
# source unpacked somewhere.
#
# Change into the `src' directory.
#   cd sawfish-x.yz/src/
#
# Run patch against this file to patch x.c:
#   patch -p1 < /path/to/x.c.patch
#
# Compile and install Sawfish.
#   make
#   make install
#
# Restart Sawfish.
#
# Alternatively, you might want to install sawfish using
# some package manager, such as apt or RPM. Then you can
# build and locally install just the patched library using
# the following technique:
#   make
#   mkdir -p ~/.sawfish/lib/sawfish/wm/util
#   cp src/.libs/x.* ~/.sawfish/lib
#   cp src/.libs/x.* ~/.sawfish/lib/sawfish/wm/util
#
# You'll also need to add the following line to the *start*
# of your ~/.sawfishrc:
#   (setq dl-load-path (cons "~/.sawfish/lib" dl-load-path))
#
# Restart Sawfish.
#
# ******************
# ** 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.
#
# - merlin

Index: src/x.c
===================================================================
RCS file: /cvs/gnome/sawfish/src/x.c,v
retrieving revision 1.23
diff -u -r1.23 x.c
--- src/x.c	3 Nov 2002 02:22:43 -0000	1.23
+++ src/x.c	10 Nov 2002 22:41:28 -0000
@@ -6,6 +6,9 @@
    Originally written by merlin <merlin@merlin.org>, with additions
    from John Harper
 
+   Then patched again by merlin to add some wicked functions:
+   x.c#pl:merlin/-0.8.6
+
    This file is part of sawmill.
 
    sawmill is free software; you can redistribute it and/or modify it
@@ -73,6 +76,7 @@
     int is_pixmap : 1;
     int is_bitmap : 1;			/* depth == 1 */
     int width, height;
+    repv plist;
 } Lisp_X_Window;
 
 #define X_XDRAWABLEP(v) rep_CELL16_TYPEP(v, x_window_type)
@@ -83,6 +87,8 @@
 #define X_PIXMAPP(v)	(X_DRAWABLEP (v) && VX_DRAWABLE (v)->is_pixmap)
 #define X_BITMAPP(v)	(X_DRAWABLEP (v) && VX_DRAWABLE (v)->is_bitmap)
 
+#define ANY_WINDOWP(w)  (rep_INTEGERP(w) || X_WINDOWP(w) || (WINDOWP(w) && VWIN(w)->id != 0))
+
 static Lisp_X_GC *x_gc_list = NULL;
 int x_gc_type;
 
@@ -116,6 +122,37 @@
 DEFSYM (clip_mask, "clip-mask");
 DEFSYM (clip_x_origin, "clip-x-origin");
 DEFSYM (clip_y_origin, "clip-y-origin");
+DEFSYM (sibling, "sibling");
+DEFSYM (stack_mode, "stack-mode");
+DEFSYM (override_redirect, "override-redirect");
+DEFSYM (save_under, "save-under");
+DEFSYM (event_mask, "event-mask");
+DEFSYM (parent, "parent");
+DEFSYM (raise_lowest, "raise-lowest");
+DEFSYM (lower_highest, "lower-highest");
+
+DEFSYM (serial, "serial");
+DEFSYM (send_event, "send-event");
+DEFSYM (window, "window");
+DEFSYM (event, "event");
+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");
+DEFSYM (above, "above");
+DEFSYM (value_mask, "value-mask");
 
 DEFSYM (LineSolid, "line-solid");
 DEFSYM (LineOnOffDash, "line-on-off-dash");
@@ -217,6 +254,54 @@
     return GXcopy;
 }
 
+static Atom
+x_symbol_atom (repv symbol) {
+  return XInternAtom (dpy, rep_STR (rep_SYM (symbol)->name), False);
+}
+
+
+/* Symbol matching Functions */
+
+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)
+            result = Fcons (Fintern (rep_string_dup (match->str), Qnil), result);
+        ++ match;
+    }
+    return result;
+}
+
+static int
+x_symbol_match (repv symbol, x_value_str *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;
+}
+
 
 /* GC Functions */
 
@@ -500,6 +585,16 @@
     return Qt;
 }
 
+DEFUN ("x-free-gc", Fx_free_gc, Sx_free_gc, (repv gc), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-free-gc::
+x-free-gc X-GC
+
+Free the X-GC. Same as x-destroy-gc.
+::end:: */
+{
+    return Fx_destroy_gc (gc);
+}
+
 DEFUN ("x-gc-p", Fx_gc_p, Sx_gc_p, (repv gc), rep_Subr1) /*
 ::doc:sawfish.wm.util.x#x-gc-p::
 x-gcp ARG
@@ -513,6 +608,15 @@
 
 /* Window functions */
 
+static x_value_str x_stack_mode_matches[] = {
+    { Above, "above" },
+    { Below, "below" },
+    { TopIf, "top-if" },
+    { BottomIf, "bottom-if" },
+    { Opposite, "opposite" },
+    { 0, 0 }
+};
+
 static long
 x_window_parse_changes (XWindowChanges *changes, repv attrs)
 {
@@ -550,6 +654,24 @@
 		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);
@@ -567,6 +689,35 @@
 	w->height = changes->height;
 }
 
+static x_value_str x_event_mask_matches[] = {
+    { KeyPressMask, "key-press" },
+    { KeyReleaseMask, "key-release" },
+    { ButtonPressMask, "button-press" },
+    { ButtonReleaseMask, "button-release" },
+    { EnterWindowMask, "enter-window" },
+    { LeaveWindowMask, "leave-window" },
+    { PointerMotionMask, "pointer-motion" },
+    { PointerMotionHintMask, "pointer-motion-hint" },
+    { Button1MotionMask, "button-1-motion" },
+    { Button2MotionMask, "button-2-motion" },
+    { Button3MotionMask, "button-3-motion" },
+    { Button4MotionMask, "button-4-motion" },
+    { Button5MotionMask, "button-5-motion" },
+    { ButtonMotionMask, "button-motion" },
+    { KeymapStateMask, "keymap-state" },
+    { ExposureMask, "exposure" },
+    { VisibilityChangeMask, "visibility-change" },
+    { StructureNotifyMask, "structure-notify" },
+    { ResizeRedirectMask, "resize-redirect" },
+    { SubstructureNotifyMask, "substructure-notify" },
+    { SubstructureRedirectMask, "substructure-redirect" },
+    { FocusChangeMask, "focus-change" },
+    { PropertyChangeMask, "property-change" },
+    { ColormapChangeMask, "colormap-change" },
+    { OwnerGrabButtonMask, "owner-grab-button" },
+    { 0, 0 }
+};
+                    
 static long
 x_window_parse_attributes (XSetWindowAttributes *attributes, repv attrs)
 {
@@ -589,6 +740,28 @@
 		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);
@@ -597,32 +770,290 @@
     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 x_value_str x_configure_mask_matches[] = {
+  { CWX, "x" },
+  { CWY, "y" },
+  { CWWidth, "width" },
+  { CWHeight, "height" },
+  { CWBorderWidth, "border-width" },
+  { CWSibling, "sibling" },
+  { CWStackMode, "stack-mode" },
+  { 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;
+}
+
+#define ALIST_PRE(A,B,C) A = Fcons (Fcons (B, C), A)
+
+static repv x_window_or_int_from_id (Window window) {
+  repv tmp = x_window_from_id (window);
+  if (tmp == Qnil)
+    tmp = rep_MAKE_INT (window);
+  return tmp;
+}
+
+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, x_window_from_id (ev->xany.window));
+
+    switch (ev->type) {
+        case KeyPress:
+        case KeyRelease:
+            ALIST_PRE (event, Qroot, x_window_or_int_from_id (ev->xkey.root));
+            ALIST_PRE (event, Qsubwindow, x_window_or_int_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, x_window_or_int_from_id (ev->xbutton.root));
+            ALIST_PRE (event, Qsubwindow, x_window_or_int_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, x_window_or_int_from_id (ev->xmotion.root));
+            ALIST_PRE (event, Qsubwindow, x_window_or_int_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, x_window_or_int_from_id (ev->xcrossing.root));
+            ALIST_PRE (event, Qsubwindow, x_window_or_int_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 DestroyNotify:
+            ALIST_PRE (event, Qevent, x_window_or_int_from_id (ev->xdestroywindow.event));
+            ALIST_PRE (event, Qwindow, x_window_or_int_from_id (ev->xdestroywindow.window));
+            break;
+            
+        case ConfigureNotify:
+            ALIST_PRE (event, Qevent, x_window_or_int_from_id (ev->xconfigure.event));
+            ALIST_PRE (event, Qwindow, x_window_or_int_from_id (ev->xconfigure.window));
+            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xconfigure.x));
+            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xconfigure.y));
+            ALIST_PRE (event, Qwidth, rep_MAKE_INT (ev->xconfigure.width));
+            ALIST_PRE (event, Qheight, rep_MAKE_INT (ev->xconfigure.height));
+            ALIST_PRE (event, Qborder_width, rep_MAKE_INT (ev->xconfigure.border_width));
+            ALIST_PRE (event, Qabove, x_window_or_int_from_id (ev->xconfigure.above));
+            ALIST_PRE (event, Qoverride_redirect, ev->xconfigure.override_redirect ? Qt : Qnil);
+            break;
+
+        case ConfigureRequest:
+            ALIST_PRE (event, Qparent, x_window_or_int_from_id (ev->xconfigurerequest.parent));
+            ALIST_PRE (event, Qwindow, x_window_or_int_from_id (ev->xconfigurerequest.window));
+            ALIST_PRE (event, Qx, rep_MAKE_INT (ev->xconfigurerequest.x));
+            ALIST_PRE (event, Qy, rep_MAKE_INT (ev->xconfigurerequest.y));
+            ALIST_PRE (event, Qwidth, rep_MAKE_INT (ev->xconfigurerequest.width));
+            ALIST_PRE (event, Qheight, rep_MAKE_INT (ev->xconfigurerequest.height));
+            ALIST_PRE (event, Qborder_width, rep_MAKE_INT (ev->xconfigurerequest.border_width));
+            ALIST_PRE (event, Qabove, x_window_or_int_from_id (ev->xconfigurerequest.above));
+            ALIST_PRE (event, Qdetail, x_value_match (ev->xconfigurerequest.detail, x_stack_mode_matches));
+            ALIST_PRE (event, Qvalue_mask, x_valuemask_match (ev->xconfigurerequest.value_mask, x_configure_mask_matches));
+            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 UnmapNotify MapNotify MapRequest ReparentNotify
+    ConfigureRequest GravityNotify ResizeRequest CirculateNotify
+    CirculateRequest PropertyNotify SelectionClear SelectionRequest
+    SelectionNotify ColormapNotify MappingNotify
+    */
+  
+    return event;
+}
+
 static void
 x_window_event_handler (XEvent *ev)
 {
     repv win = x_window_from_id (ev->xany.window);
     if (win != Qnil && VX_DRAWABLE (win)->event_handler != Qnil)
     {
-	repv type = Qnil, args = Qnil;
-	switch (ev->type)
-	{
-	case Expose:
-	    /* Since we don't provide a method of detecting which
-	       part of the window to redraw, ignore all but the last
-	       expose event. (Another option is to set the clip
-	       rectangle?) */
-	    if (ev->xexpose.count == 0)
-		type = Qexpose;
-	    break;
-
-	    /* XXX other event types..? */
-	}
-	if (type != Qnil)
-	{
-	    args = Fcons (type, Fcons (win, args));
-	    rep_funcall (VX_DRAWABLE (win)->event_handler, args, rep_FALSE);
-	}
+        repv type = x_value_match (ev->type, x_event_type_matches);
+        repv event = x_encode_event (ev);
+        repv args = Fcons (type, Fcons (win, Fcons (event, Qnil)));
+	/* Note that in Sawfish 0.34+, expose events whose count is non
+	 * zero are silently suppressed. I don't do that because I
+	 * supply the count. Which means that other people's expose
+	 * handlers will be called multiply... */
+        if (rep_funcall (VX_DRAWABLE(win)->event_handler, args, rep_FALSE) != Qnil)
+          return; /* don't call standard event-handler on non-nil result */
     }
+    
+    if (ev->type < LASTEvent && event_handlers[ev->type] != 0)
+        event_handlers[ev->type] (ev);
 }
 
 static Lisp_X_Window *
@@ -638,10 +1069,37 @@
     w->height = height;
     w->is_window = w->is_pixmap = w->is_bitmap = 0;
     w->event_handler = Qnil;
+    w->plist = Qnil;
     XSaveContext (dpy, id, x_drawable_context, (XPointer) w);
     return w;
 }
 
+DEFUN ("x-reparent-window", Fx_reparent_window, Sx_reparent_window,
+       (repv win, repv parent, repv xy), rep_Subr3) /*
+::doc:sawfish.wm.util.x#x-create-window::
+x-create-window WINDOW PARENT (X . Y)
+
+Reparents a windows.
+::end:: */
+{
+    Window _win, _parent;
+    int _x, _y;
+
+    rep_DECLARE1(win, ANY_WINDOWP);
+    rep_DECLARE (2, parent, (parent == Qnil) || ANY_WINDOWP (parent));
+    rep_DECLARE (3, xy, rep_CONSP (xy)
+		&& rep_INTP (rep_CAR (xy)) && rep_INTP (rep_CDR (xy)));
+
+    _win = window_from_arg (win);
+    _parent = (parent == Qnil) ? root_window : window_from_arg (parent);
+    _x = rep_INT (rep_CAR (xy));
+    _y = rep_INT (rep_CDR (xy));
+
+    XReparentWindow (dpy, _win, _parent, _x, _y);
+
+    return Qt;
+}
+
 DEFUN ("x-create-window", Fx_create_window, Sx_create_window,
        (repv xy, repv wh, repv bw, repv attrs, repv ev), rep_Subr5) /*
 ::doc:sawfish.wm.util.x#x-create-window::
@@ -649,12 +1107,15 @@
 
 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' and `border-color'. The
-window is created unmapped.
+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'. The window is created unmapped.
 ::end:: */
 {
     Lisp_X_Window *w;
-    Window id;
+    repv parent = Qnil;
+    Window id, _parent;
     XSetWindowAttributes attributes;
     long attributesMask;
     int _x, _y, _w, _h, _bw;
@@ -666,6 +1127,11 @@
     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));
@@ -673,19 +1139,21 @@
     _bw = rep_INT (bw);
 
     attributesMask = x_window_parse_attributes (&attributes, attrs);
-    attributes.override_redirect = True;
-    attributes.event_mask = ExposureMask;
-    attributes.colormap = image_cmap;
+    if (! (attributesMask & CWOverrideRedirect))
+    {
+        attributes.override_redirect = True;
+        attributesMask |= CWOverrideRedirect;
+    }
     if (! (attributesMask & CWBorderPixel))
     {
 	attributes.border_pixel = BlackPixel (dpy,
 					      BlackPixel (dpy, screen_num));
 	attributesMask |= CWBorderPixel;
     }
+    attributes.colormap = image_cmap;
+    attributesMask |= CWOverrideRedirect;
 
-    attributesMask |= CWOverrideRedirect | CWEventMask | CWColormap;
-
-    id = XCreateWindow (dpy, root_window, _x, _y, _w, _h, _bw,
+    id = XCreateWindow (dpy, _parent, _x, _y, _w, _h, _bw,
 			image_depth, InputOutput, image_visual,
 			attributesMask, &attributes);
 
@@ -738,17 +1206,80 @@
     return rep_VAL (w);
 }
 
+DEFUN("x-map-notify", Fx_map_notify, Sx_map_notify, (repv win), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-map-notify::
+x-map-notify X-WINDOW
+::end:: */
+{
+    XEvent fake = { MapNotify }; /* ouch the pain */
+    rep_DECLARE1(win, ANY_WINDOWP);
+    
+    fake.xmap.window = window_from_arg (win);
+    fake.xmap.event = fake.xmap.window;
+
+    event_handlers[MapNotify] (&fake);
+    
+    return Qt;
+}
+
+DEFUN("x-map-request", Fx_map_request, Sx_map_request, (repv win), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-map-request::
+x-map-request X-WINDOW
+::end:: */
+{
+    XEvent fake = { MapRequest }; /* ouch the pain */
+    rep_DECLARE1(win, ANY_WINDOWP);
+    
+    fake.xmaprequest.window = window_from_arg (win);
+
+    event_handlers[MapRequest] (&fake);
+    
+    return Qt;
+}
+
 DEFUN ("x-map-window", Fx_map_window, Sx_map_window,
        (repv win, repv unraised), rep_Subr2) /*
 ::doc:sawfish.wm.util.x#x-map-window::
 x-map-window X-WINDOW [UNRAISED]
 ::end:: */
 {
-    rep_DECLARE1 (win, X_WINDOWP);
+    rep_DECLARE1 (win, ANY_WINDOWP);
     if (unraised == Qnil)
-	XMapRaised (dpy, VX_DRAWABLE (win)->id);
+	XMapRaised (dpy, window_from_arg (win));
     else
-	XMapWindow (dpy, VX_DRAWABLE (win)->id);
+	XMapWindow (dpy, window_from_arg (win));
+    return Qt;
+}
+
+DEFUN ("x-x-map-window", Fx_x_map_window, Sx_x_map_window, (repv win), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-x-map-window::
+x-x-map-window X-WINDOW
+
+The real XMapWindow.
+::end:: */
+{
+    rep_DECLARE1 (win, ANY_WINDOWP);
+    XMapWindow (dpy, window_from_arg (win));
+    return Qt;
+}
+
+DEFUN("x-map-raised", Fx_map_raised, Sx_map_raised, (repv win), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-map-raised::
+x-map-raised X-WINDOW
+::end:: */
+{
+    rep_DECLARE1(win, ANY_WINDOWP);
+    XMapRaised (dpy, window_from_arg (win));
+    return Qt;
+}
+
+DEFUN("x-map-subwindows", Fx_map_subwindows, Sx_map_subwindows, (repv win), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-map-subwindows::
+x-map-subwindows X-WINDOW
+::end:: */
+{
+    rep_DECLARE1(win, ANY_WINDOWP);
+    XMapSubwindows (dpy, window_from_arg (win));
     return Qt;
 }
 
@@ -758,8 +1289,52 @@
 x-unmap-window X-WINDOW
 ::end:: */
 {
-    rep_DECLARE1 (win, X_WINDOWP);
-    XUnmapWindow (dpy, VX_DRAWABLE (win)->id);
+    rep_DECLARE1 (win, ANY_WINDOWP);
+    XUnmapWindow (dpy, window_from_arg (win));
+    return Qt;
+}
+
+DEFUN("x-unmap-subwindows", Fx_unmap_subwindows, Sx_unmap_subwindows, (repv win), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-unmap-subwindows::
+x-unmap-subwindows X-WINDOW
+::end:: */
+{
+    rep_DECLARE1(win, ANY_WINDOWP);
+    XUnmapSubwindows (dpy, window_from_arg (win));
+    return Qt;
+}
+
+DEFUN("x-configure-request", Fx_configure_request, Sx_configure_request, (repv window, repv attrs), rep_Subr2) /*
+::doc:sawfish.wm.util.x#x-configure-request::
+x-configure-request WINDOW ATTRS
+::end:: */
+{
+    XWindowChanges changes;
+    long changesMask;
+
+    rep_DECLARE1(window, ANY_WINDOWP);
+    rep_DECLARE2(attrs, rep_LISTP);
+
+    changesMask = x_window_parse_changes (&changes, attrs);
+
+    if (changesMask)
+    {
+      XEvent fake = { ConfigureRequest };
+      
+      fake.xconfigurerequest.display = dpy;
+      fake.xconfigurerequest.window = window_from_arg (window);
+      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;
 }
 
@@ -770,22 +1345,25 @@
 
 Reconfigures the X-WINDOW. ATTRS should be an alist mapping attribute
 names to values. Known attributes include the symbols `x', `y',
-`width', `height' and `border-width'.
+`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, X_WINDOWP);
+    rep_DECLARE1 (window, ANY_WINDOWP);
     rep_DECLARE2 (attrs, rep_LISTP);
 
     changesMask = x_window_parse_changes (&changes, attrs);
 
     if (changesMask)
     {
-	XConfigureWindow (dpy, VX_DRAWABLE (window)->id,
+	XConfigureWindow (dpy, window_from_arg (window),
 			  changesMask, &changes);
-	x_window_note_changes (VX_DRAWABLE (window), changesMask, &changes);
+        if (X_DRAWABLEP (window))
+          x_window_note_changes (VX_DRAWABLE (window), changesMask, &changes);
     }
 
     return Qt;
@@ -804,20 +1382,118 @@
     XSetWindowAttributes attributes;
     long attributesMask;
 
-    rep_DECLARE1 (window, X_WINDOWP);
+    rep_DECLARE1 (window, ANY_WINDOWP);
     rep_DECLARE2 (attrs, rep_LISTP);
 
     attributesMask = x_window_parse_attributes (&attributes, attrs);
 
     if (attributesMask)
     {
-	XChangeWindowAttributes (dpy, VX_DRAWABLE (window)->id,
+	XChangeWindowAttributes (dpy, window_from_arg (window),
 				 attributesMask, &attributes);
     }
 
     return Qt;
 }
 
+DEFUN("x-x-raise-window", Fx_x_raise_window, Sx_x_raise_window, (repv window), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-x-raise-window::
+x-x-raise-window WINDOW
+
+The real XRaiseWindow. Raises the X-WINDOW.
+::end:: */
+{
+    rep_DECLARE1(window, ANY_WINDOWP);
+
+    XRaiseWindow (dpy, window_from_arg (window));
+
+    return Qt;
+}
+
+DEFUN("x-x-lower-window", Fx_x_lower_window, Sx_x_lower_window, (repv window), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-x-lower-window::
+x-x-lower-window WINDOW
+
+The real XLowerWindow. Lowers the X-WINDOW.
+::end:: */
+{
+    rep_DECLARE1(window, ANY_WINDOWP);
+
+    XLowerWindow (dpy, window_from_arg (window));
+
+    return Qt;
+}
+
+DEFUN("x-circulate-subwindows", Fx_circulate_subwindows, Sx_circulate_subwindows, (repv window, repv direction), rep_Subr2) /*
+::doc:sawfish.wm.util.x#x-circulate-subwindows::
+x-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, ANY_WINDOWP);
+    rep_DECLARE(2, direction, (direction == Qraise_lowest) || (direction == Qlower_highest));
+    _direction = (direction == Qraise_lowest) ? RaiseLowest : LowerHighest;
+
+    XCirculateSubwindows (dpy, window_from_arg (window), _direction);
+
+    return Qt;
+}
+
+DEFUN("x-circulate-subwindows-up", Fx_circulate_subwindows_up, Sx_circulate_subwindows_up, (repv window), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-circulate-subwindows-up::
+x-circulate-subwindows-up WINDOW
+
+Circulates up the subwindows of the X-WINDOW.
+::end:: */
+{
+    rep_DECLARE1(window, ANY_WINDOWP);
+
+    XCirculateSubwindowsUp (dpy, window_from_arg (window));
+
+    return Qt;
+}
+
+DEFUN("x-circulate-subwindows-down", Fx_circulate_subwindows_down, Sx_circulate_subwindows_down, (repv window), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-circulate-subwindows-down::
+x-circulate-subwindows-down WINDOW
+
+Circulates down the subwindows of the X-WINDOW.
+::end:: */
+{
+    rep_DECLARE1(window, ANY_WINDOWP);
+
+    XCirculateSubwindowsDown (dpy, window_from_arg (window));
+
+    return Qt;
+}
+
+DEFUN("x-restack-windows", Fx_restack_windows, Sx_restack_windows, (repv list), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-restack-windows::
+x-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 (X_WINDOWP (rep_CAR (list)))
+        windows[n ++] = window_from_arg (rep_CAR (list));
+      list = rep_CDR (list);
+    }
+    XRestackWindows (dpy, windows, n);
+
+    return Qt;
+}
+
 DEFUN ("x-destroy-drawable", Fx_destroy_drawable,
        Sx_destroy_drawable, (repv drawable), rep_Subr1) /*
 ::doc:sawfish.wm.util.x#x-destroy-drawable::
@@ -849,7 +1525,7 @@
 Destroys the X-WINDOW.
 ::end:: */
 {
-    return Fx_destroy_drawable (window);
+    return Fx_destroy_drawable (window); /* TODO: window_from_arg and below? */
 }
 
 DEFUN ("x-drawable-id", Fx_drawable_id,
@@ -989,6 +1665,327 @@
 }
 
 
+/* Lisp property functions */
+
+DEFUN ("x-window-put", Fx_window_put, Sx_window_put, (repv window, repv key, repv value), rep_Subr3) /*
+::doc:sawfish.wm.util.x#x-window-put::
+x-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, X_WINDOWP);
+    rep_DECLARE2(key, rep_SYMBOLP);
+
+    ptr = plist = VX_DRAWABLE(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);
+    }
+    VX_DRAWABLE(window)->plist = Fcons (Fcons (key, value), plist);
+    
+    return Qt;
+}
+
+DEFUN ("x-window-get", Fx_window_get, Sx_window_get, (repv window, repv key), rep_Subr2) /*
+::doc:sawfish.wm.util.x#x-window-get::
+x-window-get WINDOW KEY
+
+Gets the value stored in the specified WINDOW under the specified
+(symbolic) KEY.
+::end:: */
+{
+    repv plist, ptr;
+  
+    rep_DECLARE1(window, X_WINDOWP);
+    rep_DECLARE2(key, rep_SYMBOLP);
+
+    ptr = plist = VX_DRAWABLE(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;
+}
+
+
+/* X property functions */
+
+DEFUN("x-set-text-property", Fx_set_text_property, Sx_set_text_property, (repv window, repv textv, repv property), rep_Subr3) /*
+::doc:sawfish.wm.util.x#x-set-text-property::
+x-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, ANY_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 = x_symbol_atom (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, window_from_arg (window), &textprop, _prop);
+    XFree (textprop.value);
+
+    return Qt;
+}
+
+DEFUN("x-get-text-property", Fx_get_text_property, Sx_get_text_property, (repv window, repv property), rep_Subr2) /*
+::doc:sawfish.wm.util.x#x-get-text-property::
+x-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, ANY_WINDOWP);
+    rep_DECLARE2 (property, rep_SYMBOLP);
+    
+    _prop = x_symbol_atom (property);
+    if (!XGetTextProperty (dpy, window_from_arg (window), &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("x-list-properties", Fx_list_properties, Sx_list_properties, (repv window), rep_Subr1) /*
+::doc:sawfish.wm.util.x#x-list-properties::
+x-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, ANY_WINDOWP);
+
+    atoms = XListProperties (dpy, window_from_arg (window), &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_value_str x_change_property_mode_matches[] = {
+    { PropModeReplace, "prop-mode-replace" },
+    { PropModePrepend, "prop-mode-prepend" },
+    { PropModeAppend, "prop-mode-append" },
+    { 0, 0 }
+};
+
+#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);\
+}
+
+DEFUN("x-change-property", Fx_change_property, Sx_change_property, (repv args), rep_SubrN) /*
+::doc:sawfish.wm.util.x#x-change-property::
+x-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, ANY_WINDOWP (window));
+    _window = window_from_arg (window);
+    nDECLARE (2, property, rep_SYMBOLP (property));
+    _property = x_symbol_atom (property);
+    nDECLARE (3, type, rep_SYMBOLP (type));
+    _type = x_symbol_atom (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("x-rotate-window-properties", Fx_rotate_window_properties, Sx_rotate_window_properties, (repv window, repv list, repv npos), rep_Subr3) /*
+::doc:sawfish.wm.util.x#x-rotate-window-properties::
+x-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, ANY_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 ++] = x_symbol_atom (rep_CAR (list));
+      list = rep_CDR (list);
+    }
+    XRotateWindowProperties (dpy, window_from_arg (window), atoms, n, _npos);
+
+    return Qt;
+}
+
+DEFUN("x-delete-property", Fx_delete_property, Sx_delete_property, (repv window, repv property), rep_Subr2) /*
+::doc:sawfish.wm.util.x#x-delete-property::
+x-delete-property X-WINDOW PROPERTY
+
+Deletes the specified PROPERTY from the specified X-WINDOW.
+::end:: */
+{
+    Atom _prop;
+  
+    rep_DECLARE1 (window, ANY_WINDOWP);
+    rep_DECLARE2 (property, rep_SYMBOLP);
+    
+    _prop = x_symbol_atom (property);
+    XDeleteProperty (dpy, window_from_arg (window), _prop);
+
+    return Qt;
+}
+
+DEFUN("x-get-window-property", Fx_get_window_property, Sx_get_window_property, (repv args), rep_SubrN) /*
+::doc:sawfish.wm.util.x#x-get-window-property::
+x-get-window-property X-WINDOW PROPERTY LONG-OFFSET LONG-LENGTH DELETE TYPE
+
+Gets the specified PROPERTY from the specified X-WINDOW. If
+DELETE is non-nil then the property is deleted. The return
+is a list (type format bytes-after [value]).
+::end:: */
+{
+    repv window, property, offset, length, delete, type, format, nbytes, data;
+    Window _window;
+    Atom _property, _type, _ret_type;
+    Bool _delete;
+    long _offset, _length;
+    int _ret_fmt;
+    unsigned long _ret_nitems, _ret_nbytes;
+    unsigned char *_ret;
+    int i, n;
+
+    /* I'm careless with my ints and longs */
+
+    nDECLARE (1, window, ANY_WINDOWP (window));
+    _window = window_from_arg (window);
+    nDECLARE (2, property, rep_SYMBOLP (property));
+    _property = x_symbol_atom (property);
+    nDECLARE (3, offset, rep_INTP (offset));
+    _offset = rep_INT (offset);
+    nDECLARE (4, length, rep_INTP (length));
+    _length = rep_INT (length);
+    nDECLARE (5, delete, 1);
+    _delete = delete != Qnil;
+    nDECLARE (6, type, rep_SYMBOLP (type));
+    _type = x_symbol_atom (type);
+
+    XGetWindowProperty (dpy, _window, _property, _offset, _length, _delete,
+        _type, &_ret_type, &_ret_fmt, &_ret_nitems, &_ret_nbytes, &_ret);
+
+    type = x_atom_symbol (_ret_type);
+    format = rep_MAKE_INT (_ret_fmt);
+    nbytes = rep_MAKE_INT (_ret_nbytes);
+    n = (int) _ret_nitems;
+    data = Fmake_vector (rep_MAKE_INT (n), Qnil);
+    for (i = 0; i < n; ++ i) {
+      switch (_ret_fmt) {
+        case 8:
+          rep_VECTI (data, i) = rep_MAKE_INT (_ret[i]);
+          break;
+        case 16:
+          rep_VECTI (data, i) = rep_MAKE_INT (((unsigned short *) _ret)[i]);
+          break;
+        case 32:
+          rep_VECTI (data, i) = rep_MAKE_INT (((unsigned int *) _ret)[i]);
+          break;
+      }
+    }
+
+    return Fcons (type, Fcons (format, Fcons (nbytes, Fcons (data, Qnil))));
+}
+
+
 /* Drawing functions */
 
 DEFUN ("x-clear-window", Fx_clear_window,
@@ -1456,6 +2453,7 @@
 x_window_mark (repv obj)
 {
     rep_MARKVAL (VX_DRAWABLE (obj)->event_handler);
+    rep_MARKVAL (VX_DRAWABLE (obj)->plist);
 }
 
 static void
@@ -1501,6 +2499,7 @@
     rep_ADD_SUBR (Sx_create_root_xor_gc);
     rep_ADD_SUBR (Sx_change_gc);
     rep_ADD_SUBR (Sx_destroy_gc);
+    rep_ADD_SUBR (Sx_free_gc);
     rep_ADD_SUBR (Sx_gc_p);
 
     x_drawable_context = XUniqueContext ();
@@ -1510,12 +2509,26 @@
 					   x_window_sweep, x_window_mark,
 					   0, 0, 0, 0, 0, 0, 0);
     rep_ADD_SUBR (Sx_create_window);
+    rep_ADD_SUBR (Sx_reparent_window);
     rep_ADD_SUBR (Sx_create_pixmap);
     rep_ADD_SUBR (Sx_create_bitmap);
+    rep_ADD_SUBR (Sx_map_request);
+    rep_ADD_SUBR (Sx_map_notify);
     rep_ADD_SUBR (Sx_map_window);
+    rep_ADD_SUBR (Sx_x_map_window);
+    rep_ADD_SUBR (Sx_map_raised);
+    rep_ADD_SUBR (Sx_map_subwindows);
     rep_ADD_SUBR (Sx_unmap_window);
+    rep_ADD_SUBR (Sx_unmap_subwindows);
+    rep_ADD_SUBR (Sx_configure_request);
     rep_ADD_SUBR (Sx_configure_window);
     rep_ADD_SUBR (Sx_change_window_attributes);
+    rep_ADD_SUBR (Sx_x_raise_window);
+    rep_ADD_SUBR (Sx_x_lower_window);
+    rep_ADD_SUBR (Sx_circulate_subwindows);
+    rep_ADD_SUBR (Sx_circulate_subwindows_up);
+    rep_ADD_SUBR (Sx_circulate_subwindows_down);
+    rep_ADD_SUBR (Sx_restack_windows);
     rep_ADD_SUBR (Sx_destroy_drawable);
     rep_ADD_SUBR (Sx_destroy_window);
     rep_ADD_SUBR (Sx_drawable_p);
@@ -1529,6 +2542,17 @@
     rep_ADD_SUBR (Sx_window_back_buffer);
     rep_ADD_SUBR (Sx_window_swap_buffers);
 
+    rep_ADD_SUBR (Sx_window_put);
+    rep_ADD_SUBR (Sx_window_get);
+
+    rep_ADD_SUBR (Sx_set_text_property);
+    rep_ADD_SUBR (Sx_get_text_property);
+    rep_ADD_SUBR (Sx_get_window_property);
+    rep_ADD_SUBR (Sx_list_properties);
+    rep_ADD_SUBR (Sx_change_property);
+    rep_ADD_SUBR (Sx_rotate_window_properties);
+    rep_ADD_SUBR (Sx_delete_property);
+
     rep_ADD_SUBR (Sx_clear_window);
     rep_ADD_SUBR (Sx_draw_string);
     rep_ADD_SUBR (Sx_draw_line);
@@ -1565,6 +2589,37 @@
     rep_INTERN (clip_mask);
     rep_INTERN (clip_x_origin);
     rep_INTERN (clip_y_origin);
+    rep_INTERN (sibling);
+    rep_INTERN (stack_mode);
+    rep_INTERN (override_redirect);
+    rep_INTERN (save_under);
+    rep_INTERN (event_mask);
+    rep_INTERN (parent);
+ 
+    rep_INTERN (serial);
+    rep_INTERN (send_event);
+    rep_INTERN (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 (above);
+    rep_INTERN (value_mask);
+    rep_INTERN (raise_lowest);
+    rep_INTERN (lower_highest);
 
     rep_INTERN (LineSolid);
     rep_INTERN (LineOnOffDash);
