From 7976ee51afcad41b611e642d2feb31d805dedcf6 Mon Sep 17 00:00:00 2001
From: Kevin E Martin <kem@kem.org>
Date: Wed, 30 Jun 2004 20:06:56 +0000
Subject: Add Distributed Multihead X (DMX) support

---
 hw/dmx/config/Canvas.c       |  160 ++++++
 hw/dmx/config/Canvas.h       |   56 ++
 hw/dmx/config/CanvasP.h      |   66 +++
 hw/dmx/config/TODO           |    7 +
 hw/dmx/config/dmxcompat.c    |  233 +++++++++
 hw/dmx/config/dmxcompat.h    |   45 ++
 hw/dmx/config/dmxconfig.c    |  495 ++++++++++++++++++
 hw/dmx/config/dmxconfig.h    |   65 +++
 hw/dmx/config/dmxparse.c     |  607 +++++++++++++++++++++
 hw/dmx/config/dmxparse.h     |  298 +++++++++++
 hw/dmx/config/dmxprint.c     |  452 ++++++++++++++++
 hw/dmx/config/dmxprint.h     |   44 ++
 hw/dmx/config/dmxtodmx.c     |   50 ++
 hw/dmx/config/dmxtodmx.man   |   41 ++
 hw/dmx/config/parser.y       |  222 ++++++++
 hw/dmx/config/scanner.l      |  179 +++++++
 hw/dmx/config/test-a.in      |    1 +
 hw/dmx/config/test-a.out     |    2 +
 hw/dmx/config/test-b.in      |    1 +
 hw/dmx/config/test-b.out     |    1 +
 hw/dmx/config/test-c.in      |    1 +
 hw/dmx/config/test-c.out     |    2 +
 hw/dmx/config/test-d.in      |    1 +
 hw/dmx/config/test-d.out     |    2 +
 hw/dmx/config/test-e.in      |    1 +
 hw/dmx/config/test-e.out     |    2 +
 hw/dmx/config/test-f.in      |    2 +
 hw/dmx/config/test-f.out     |  Bin 0 -> 69 bytes
 hw/dmx/config/test-g.in      |    4 +
 hw/dmx/config/test-g.out     |    4 +
 hw/dmx/config/test-h.in      |    7 +
 hw/dmx/config/test-h.out     |    7 +
 hw/dmx/config/test-i.in      |    3 +
 hw/dmx/config/test-i.out     |    3 +
 hw/dmx/config/test-j.in      |   13 +
 hw/dmx/config/test-j.out     |   11 +
 hw/dmx/config/test-k.in      |    3 +
 hw/dmx/config/test-k.out     |    3 +
 hw/dmx/config/test-l.in      |   12 +
 hw/dmx/config/test-l.out     |   12 +
 hw/dmx/config/vdltodmx.c     |   58 +++
 hw/dmx/config/vdltodmx.man   |   95 ++++
 hw/dmx/config/xdmxconfig.c   | 1188 ++++++++++++++++++++++++++++++++++++++++++
 hw/dmx/config/xdmxconfig.man |   63 +++
 44 files changed, 4522 insertions(+)
 create mode 100644 hw/dmx/config/Canvas.c
 create mode 100644 hw/dmx/config/Canvas.h
 create mode 100644 hw/dmx/config/CanvasP.h
 create mode 100644 hw/dmx/config/TODO
 create mode 100644 hw/dmx/config/dmxcompat.c
 create mode 100644 hw/dmx/config/dmxcompat.h
 create mode 100644 hw/dmx/config/dmxconfig.c
 create mode 100644 hw/dmx/config/dmxconfig.h
 create mode 100644 hw/dmx/config/dmxparse.c
 create mode 100644 hw/dmx/config/dmxparse.h
 create mode 100644 hw/dmx/config/dmxprint.c
 create mode 100644 hw/dmx/config/dmxprint.h
 create mode 100644 hw/dmx/config/dmxtodmx.c
 create mode 100644 hw/dmx/config/dmxtodmx.man
 create mode 100644 hw/dmx/config/parser.y
 create mode 100644 hw/dmx/config/scanner.l
 create mode 100644 hw/dmx/config/test-a.in
 create mode 100644 hw/dmx/config/test-a.out
 create mode 100644 hw/dmx/config/test-b.in
 create mode 100644 hw/dmx/config/test-b.out
 create mode 100644 hw/dmx/config/test-c.in
 create mode 100644 hw/dmx/config/test-c.out
 create mode 100644 hw/dmx/config/test-d.in
 create mode 100644 hw/dmx/config/test-d.out
 create mode 100644 hw/dmx/config/test-e.in
 create mode 100644 hw/dmx/config/test-e.out
 create mode 100644 hw/dmx/config/test-f.in
 create mode 100644 hw/dmx/config/test-f.out
 create mode 100644 hw/dmx/config/test-g.in
 create mode 100644 hw/dmx/config/test-g.out
 create mode 100644 hw/dmx/config/test-h.in
 create mode 100644 hw/dmx/config/test-h.out
 create mode 100644 hw/dmx/config/test-i.in
 create mode 100644 hw/dmx/config/test-i.out
 create mode 100644 hw/dmx/config/test-j.in
 create mode 100644 hw/dmx/config/test-j.out
 create mode 100644 hw/dmx/config/test-k.in
 create mode 100644 hw/dmx/config/test-k.out
 create mode 100644 hw/dmx/config/test-l.in
 create mode 100644 hw/dmx/config/test-l.out
 create mode 100644 hw/dmx/config/vdltodmx.c
 create mode 100644 hw/dmx/config/vdltodmx.man
 create mode 100644 hw/dmx/config/xdmxconfig.c
 create mode 100644 hw/dmx/config/xdmxconfig.man

(limited to 'hw/dmx/config')

diff --git a/hw/dmx/config/Canvas.c b/hw/dmx/config/Canvas.c
new file mode 100644
index 000000000..4dccd7382
--- /dev/null
+++ b/hw/dmx/config/Canvas.c
@@ -0,0 +1,160 @@
+/* $XFree86$ */
+/*
+ * Copyright 1987, 1998  The Open Group
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of The Open Group shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from The Open Group.
+ */
+
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ * This file was originally taken from xc/lib/Xaw/Template.c
+ */
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include "CanvasP.h"
+
+static void CanvasInitialize(Widget request, Widget w,
+                             ArgList args, Cardinal *num_args)
+{
+}
+
+static void CanvasExpose(Widget w, XEvent *event, Region region)
+{
+    CanvasExposeDataRec data;
+
+    data.w      = w;
+    data.event  = event;
+    data.region = region;
+    
+    if (!XtIsRealized(w)) return;
+    XtCallCallbacks(w, XtNcanvasExposeCallback, (XtPointer)&data);
+}
+
+static void CanvasResize(Widget w)
+{
+    if (!XtIsRealized(w)) return;
+    XtCallCallbacks(w, XtNcanvasResizeCallback, (XtPointer)w);
+}
+
+static void CanvasAction(Widget w, XEvent *event,
+                         String *params, Cardinal *num_params)
+{
+    XtCallCallbacks(w, XtNcallback, (XtPointer)event);
+}
+
+#define offset(field) XtOffsetOf(CanvasRec, canvas.field)
+static XtResource resources[] = {
+    { XtNcallback, XtCCallback, XtRCallback,
+      sizeof(XtCallbackList), offset(input_callback), XtRCallback, NULL },
+    { XtNcanvasExposeCallback, XtCcanvasExposeCallback, XtRCallback,
+      sizeof(XtCallbackList), offset(expose_callback), XtRCallback, NULL },
+    { XtNcanvasResizeCallback, XtCcanvasResizeCallback, XtRCallback,
+      sizeof(XtCallbackList), offset(resize_callback), XtRCallback, NULL },
+};
+#undef offset
+
+static XtActionsRec actions[] =
+{
+    {"canvas",	CanvasAction},
+};
+
+static char translations[] =
+"<Key>:    canvas()\n\
+<Motion>:  canvas()\n\
+<BtnDown>: canvas()\n\
+<BtnUp>: canvas()\n\
+"
+;
+
+#define Superclass	(&widgetClassRec)
+CanvasClassRec canvasClassRec = {
+    /* core */
+    {
+        (WidgetClass)Superclass,        /* superclass */
+        "Canvas",			/* class_name */
+        sizeof(CanvasRec),		/* widget_size */
+        NULL,				/* class_initialize */
+        NULL,				/* class_part_initialize */
+        False,				/* class_inited */
+        CanvasInitialize,		/* initialize */
+        NULL,				/* initialize_hook */
+        XtInheritRealize,		/* realize */
+        actions,			/* actions */
+        XtNumber(actions),		/* num_actions */
+        resources,			/* resources */
+        XtNumber(resources),		/* num_resources */
+        NULLQUARK,			/* xrm_class */
+        True,				/* compress_motion */
+        True,				/* compress_exposure */
+        True,				/* compress_enterleave */
+        False,				/* visible_interest */
+        NULL,				/* destroy */
+        CanvasResize,			/* resize */
+        CanvasExpose,                   /* expose */
+        NULL,				/* set_values */
+        NULL,				/* set_values_hook */
+        XtInheritSetValuesAlmost,	/* set_values_almost */
+        NULL,				/* get_values_hook */
+        NULL,				/* accept_focus */
+        XtVersion,			/* version */
+        NULL,				/* callback_private */
+        translations,			/* tm_table */
+        XtInheritQueryGeometry,		/* query_geometry */
+        XtInheritDisplayAccelerator,	/* display_accelerator */
+        NULL,				/* extension */
+    },
+    /* canvas */
+    {
+        NULL,				/* extension */
+    }
+};
+
+WidgetClass canvasWidgetClass = (WidgetClass)&canvasClassRec;
diff --git a/hw/dmx/config/Canvas.h b/hw/dmx/config/Canvas.h
new file mode 100644
index 000000000..a36851a6d
--- /dev/null
+++ b/hw/dmx/config/Canvas.h
@@ -0,0 +1,56 @@
+/* $XFree86$ */
+/*
+
+Copyright 1987, 1998  The Open Group
+Copyright 2002 Red Hat Inc., Durham, North Carolina.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ * This file was originally taken from xc/lib/Xaw/Template.h
+ */
+
+#ifndef _Canvas_h
+#define _Canvas_h
+
+#include <X11/Intrinsic.h>
+
+#define XtNcanvasExposeCallback "canvasExposeCallback"
+#define XtCcanvasExposeCallback "CanvasExposeCallback"
+#define XtNcanvasResizeCallback "canvasResizeCallback"
+#define XtCcanvasResizeCallback "CanvasResizeCallback"
+
+typedef struct _CanvasClassRec *CanvasWidgetClass;
+typedef struct _CanvasRec *CanvasWidget;
+extern WidgetClass canvasWidgetClass;
+
+typedef struct _CanvasExposeDataRec {
+    Widget       w;
+    XEvent       *event;
+    Region       region;
+} CanvasExposeDataRec, *CanvasExposeDataPtr;
+
+#endif /* _Canvas_h */
diff --git a/hw/dmx/config/CanvasP.h b/hw/dmx/config/CanvasP.h
new file mode 100644
index 000000000..98f95b94e
--- /dev/null
+++ b/hw/dmx/config/CanvasP.h
@@ -0,0 +1,66 @@
+/* $XFree86$ */
+/*
+
+Copyright 1987, 1998  The Open Group
+Copyright 2002 Red Hat Inc., Durham, North Carolina.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ * This file was originally taken from xc/lib/Xaw/TemplateP.h
+ */
+
+#ifndef _CanvasP_h
+#define _CanvasP_h
+
+#include "Canvas.h"
+
+/* include superclass private header file */
+#include <X11/CoreP.h>
+
+typedef struct {
+    XtPointer extension;
+} CanvasClassPart;
+
+typedef struct _CanvasClassRec {
+    CoreClassPart	core_class;
+    CanvasClassPart	canvas_class;
+} CanvasClassRec;
+
+extern CanvasClassRec canvasClassRec;
+
+typedef struct {
+    XtCallbackList input_callback;
+    XtCallbackList expose_callback;
+    XtCallbackList resize_callback;
+} CanvasPart;
+
+typedef struct _CanvasRec {
+    CorePart	core;
+    CanvasPart	canvas;
+} CanvasRec;
+
+#endif /* _CanvasP_h */
diff --git a/hw/dmx/config/TODO b/hw/dmx/config/TODO
new file mode 100644
index 000000000..2998ff4a0
--- /dev/null
+++ b/hw/dmx/config/TODO
@@ -0,0 +1,7 @@
+Fri May 31 13:20:17 2002
+
+1) Sanitize values from input boxes.
+
+2) Add canvas colors to cavas widget resources or to command-line options.
+
+3) Add ability to edit option line(s) and wall.
diff --git a/hw/dmx/config/dmxcompat.c b/hw/dmx/config/dmxcompat.c
new file mode 100644
index 000000000..3e6e6daeb
--- /dev/null
+++ b/hw/dmx/config/dmxcompat.c
@@ -0,0 +1,233 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ */
+
+/** \file
+ * This file provides some compatibility support for reading VDL files
+ * that are used by xmovie
+ * (http://www.llnl.gov/icc/sdd/img/xmovie/xmovie.shtml).
+ *
+ * This file is not used by the DMX server.
+ */
+
+#include "dmxconfig.h"
+#include "dmxparse.h"
+#include "dmxcompat.h"
+#include "parser.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+static int dmxVDLReadLine(FILE *str, char *buf, int len)
+{
+    if (fgets(buf, len, str)) return strlen(buf);
+    return 0;
+}
+
+static int dmxVDLCount(const char *buf)
+{
+    return strtol(buf, NULL, 10);
+}
+
+static void dmxVDLVirtualEntry(const char *buf,
+                               char *name, int *len,
+                               int *x, int *y)
+{
+    char       *end;
+    const char *s;
+    char       *d;
+    int        start;
+    
+    *x = strtol(buf, &end, 10);
+    *y = strtol(end, &end, 10);
+
+    for (s = end, d = name, start = 1; *s && *s != '['; ++s) {
+        if (start && isspace(*s)) continue;
+        *d++  = *s;
+        start = 0;
+    }
+    *d = '\0';
+    while (d > name && isspace(d[-1])) *--d = '\0'; /* remove trailing space */
+    *len = strlen(name);
+}
+
+static void dmxVDLDisplayEntry(const char *buf,
+                               char *name, int *len,
+                               int *x, int *y,
+                               int *xoff, int *yoff,
+                               int *xorig, int *yorig)
+{
+    const char *pt;
+    char       *end;
+
+    pt   = strchr(buf, ' ');
+    strncpy(name, buf, pt-buf);
+    name[pt-buf] = '\0';
+    *len  = strlen(name);
+    
+    *x     = strtol(pt, &end, 10);
+    *y     = strtol(end, &end, 10);
+    *xorig = strtol(end, &end, 10);
+    *yorig = strtol(end, &end, 10);
+    *xoff  = strtol(end, &end, 10);
+    *yoff  = strtol(end, NULL, 10);
+}
+
+/** Read from the VDL format \a filename and return a newly allocated \a
+ * DMXConfigEntryPtr */
+DMXConfigEntryPtr dmxVDLRead(const char *filename)
+{
+    FILE                *str;
+    char                buf[2048]; /* RATS: Use ok */
+    char                *pt;
+    int                 lineno  = 0;
+    DMXConfigEntryPtr   entry   = NULL;
+    DMXConfigVirtualPtr virtual = NULL;
+    DMXConfigSubPtr     sub     = NULL;
+    DMXConfigDisplayPtr display = NULL;
+    DMXConfigFullDimPtr fdim    = NULL;
+    int                 vcount  = 0;
+    int                 dcount  = 0;
+    int                 icount  = 0;
+    int                 x, y, xoff, yoff, xorig, yorig;
+    char                name[2048]; /* RATS: Use ok */
+    const char          *tmp;
+    int                 len;
+    enum {
+        simulateFlag,
+        virtualCount,
+        virtualEntry,
+        displayCount,
+        displayEntry,
+        ignoreCount,
+        ignoreEntry
+    }                 state = simulateFlag;
+
+    if (!filename) str = stdin;
+    else           str = fopen(filename, "r");
+    if (!str) return NULL;
+
+    while (dmxVDLReadLine(str, buf, sizeof(buf))) {
+        DMXConfigCommentPtr comment = NULL;
+        
+        ++lineno;
+        for (pt = buf; *pt; pt++)
+            if (*pt == '\r' || *pt == '\n') {
+                *pt = '\0';
+                break;
+            }
+        if (buf[0] == '#') {
+            tmp = dmxConfigCopyString(buf + 1, strlen(buf + 1));
+            comment = dmxConfigCreateComment(T_COMMENT, lineno, tmp);
+            entry = dmxConfigAddEntry(entry, dmxConfigComment, comment, NULL);
+            continue;
+        }
+        switch (state) {
+        case simulateFlag:
+            state = virtualCount;
+            break;
+        case virtualCount:
+            vcount = dmxVDLCount(buf);
+            state = virtualEntry;
+            break;
+        case virtualEntry:
+            len = sizeof(name);
+            dmxVDLVirtualEntry(buf, name, &len, &x, &y);
+            tmp     = dmxConfigCopyString(name, len);
+            virtual = dmxConfigCreateVirtual(NULL,
+                                             dmxConfigCreateString(T_STRING,
+                                                                   lineno,
+                                                                   NULL,
+                                                                   tmp),
+                                             dmxConfigCreatePair(T_DIMENSION,
+                                                                 lineno,
+                                                                 NULL,
+                                                                 x, y, 0, 0),
+                                             NULL, NULL, NULL);
+            state = displayCount;
+            break;
+        case displayCount:
+            dcount = dmxVDLCount(buf);
+            state = displayEntry;
+            break;
+        case displayEntry:
+            dmxVDLDisplayEntry(buf, name, &len, &x, &y, &xoff, &yoff,
+                               &xorig, &yorig);
+            tmp     = dmxConfigCopyString(name, len);
+            fdim    = dmxConfigCreateFullDim(
+                dmxConfigCreatePartDim(
+                    dmxConfigCreatePair(T_DIMENSION,
+                                        lineno,
+                                        NULL,
+                                        x, y, 0, 0),
+                    dmxConfigCreatePair(T_OFFSET,
+                                        lineno,
+                                        NULL,
+                                        xoff, yoff,
+                                        xoff, yoff)),
+                NULL);
+            display = dmxConfigCreateDisplay(NULL,
+                                             dmxConfigCreateString(T_STRING,
+                                                                   lineno,
+                                                                   NULL,
+                                                                   tmp),
+                                             fdim,
+                                             dmxConfigCreatePair(T_ORIGIN,
+                                                                 lineno,
+                                                                 NULL,
+                                                                 xorig, yorig,
+                                                                 0, 0),
+                                             NULL);
+            sub = dmxConfigAddSub(sub, dmxConfigSubDisplay(display));
+            if (!--dcount) {
+                state             = ignoreCount;
+                virtual->subentry = sub;
+                entry             = dmxConfigAddEntry(entry,
+                                                      dmxConfigVirtual,
+                                                      NULL,
+                                                      virtual);
+                virtual           = NULL;
+                sub               = NULL;
+            }
+            break;
+        case ignoreCount:
+            icount = dmxVDLCount(buf);
+            state = ignoreEntry;
+            break;
+        case ignoreEntry:
+            if (!--icount) state = virtualEntry;
+            break;
+        }
+    }
+    return entry;
+}
diff --git a/hw/dmx/config/dmxcompat.h b/hw/dmx/config/dmxcompat.h
new file mode 100644
index 000000000..416ec7873
--- /dev/null
+++ b/hw/dmx/config/dmxcompat.h
@@ -0,0 +1,45 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ * Interface to VDL compatibility support.  \see dmxcompat.c
+ *
+ * This file is not used by the DMX server.
+ */
+
+#ifndef _DMXCOMPAT_H_
+#define _DMXCOMPAT_H_
+
+extern DMXConfigEntryPtr dmxVDLRead(const char *filename);
+#endif
diff --git a/hw/dmx/config/dmxconfig.c b/hw/dmx/config/dmxconfig.c
new file mode 100644
index 000000000..f276283a3
--- /dev/null
+++ b/hw/dmx/config/dmxconfig.c
@@ -0,0 +1,495 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ * Provides interface for reading DMX configuration files and for
+ * combining that information with command-line configuration parameters. */
+    
+
+#include "dmx.h"
+#include "dmxinput.h"
+#include "dmxconfig.h"
+#include "dmxparse.h"
+#include "dmxlog.h"
+#include "dmxcb.h"
+#include "dmxstat.h"
+#include "parser.h"
+
+extern int  yyparse(void);
+extern FILE *yyin;
+
+static char *dmxXkbRules;
+static char *dmxXkbModel;
+static char *dmxXkbLayout;
+static char *dmxXkbVariant;
+static char *dmxXkbOptions;
+
+/** Stores lists of configuration information. */
+typedef struct DMXConfigListStruct {
+    const char                 *name;
+    struct DMXConfigListStruct *next;
+} DMXConfigList, *DMXConfigListPtr;
+
+/** This stucture stores the parsed configuration information. */
+typedef struct DMXConfigCmdStruct {
+    const char    *filename;
+    const char    *config;
+    DMXConfigList *displays;
+    DMXConfigList *inputs;
+    DMXConfigList *xinputs;
+} DMXConfigCmd, *DMXConfigCmdPtr;
+
+DMXConfigEntryPtr    dmxConfigEntry;
+static DMXConfigCmd  dmxConfigCmd;
+
+static int dmxDisplaysFromCommandLine;
+
+/** Make a note that \a display is the name of an X11 display that
+ * should be initialized as a backend (output) display.  Called from
+ * #ddxProcessArgument. */
+void dmxConfigStoreDisplay(const char *display)
+{
+    DMXConfigListPtr entry = malloc(sizeof(*entry));
+    entry->name = strdup(display);
+    entry->next = NULL;
+    if (!dmxConfigCmd.displays) dmxConfigCmd.displays = entry;
+    else {
+        DMXConfigList *pt;
+        for (pt = dmxConfigCmd.displays; pt->next; pt = pt->next);
+        if (!pt)
+            dmxLog(dmxFatal, "dmxConfigStoreDisplay: end of list non-NULL\n");
+        pt->next = entry;
+    }
+    ++dmxDisplaysFromCommandLine;
+}
+
+/** Make a note that \a input is the name of an X11 display that should
+ * be used for input (either a backend or a console input device). */
+void dmxConfigStoreInput(const char *input)
+{
+    DMXConfigListPtr entry = malloc(sizeof(*entry));
+    entry->name = strdup(input);
+    entry->next = NULL;
+    if (!dmxConfigCmd.inputs) dmxConfigCmd.inputs = entry;
+    else {
+        DMXConfigList *pt;
+        for (pt = dmxConfigCmd.inputs; pt->next; pt = pt->next);
+        if (!pt)
+            dmxLog(dmxFatal, "dmxConfigStoreInput: end of list non-NULL\n");
+        pt->next = entry;
+    }
+}
+
+/** Make a note that \a input is the name of an X11 display that should
+ * be used for input from XInput extension devices. */
+void dmxConfigStoreXInput(const char *input)
+{
+    DMXConfigListPtr entry = malloc(sizeof(*entry));
+    entry->name = strdup(input);
+    entry->next = NULL;
+    if (!dmxConfigCmd.xinputs) dmxConfigCmd.xinputs = entry;
+    else {
+        DMXConfigList *pt;
+        for (pt = dmxConfigCmd.xinputs; pt->next; pt = pt->next);
+        if (!pt)
+            dmxLog(dmxFatal, "dmxConfigStoreXInput: end of list non-NULL\n");
+        pt->next = entry;
+    }
+}
+
+/** Make a note that \a file is the configuration file. */
+void dmxConfigStoreFile(const char *file)
+{
+    if (dmxConfigCmd.filename)
+        dmxLog(dmxFatal, "Only one -configfile allowed\n");
+    dmxConfigCmd.filename = strdup(file);
+}
+
+/** Make a note that \a config should be used as the configuration for
+ * current instantiation of the DMX server. */
+void dmxConfigStoreConfig(const char *config)
+{
+    if (dmxConfigCmd.config) dmxLog(dmxFatal, "Only one -config allowed\n");
+    dmxConfigCmd.config = strdup(config);
+}
+
+static int dmxConfigReadFile(const char *filename, int debug)
+{
+    FILE *str;
+
+    if (!(str = fopen(filename, "r"))) return -1;
+    dmxLog(dmxInfo, "Reading configuration file \"%s\"\n", filename);
+    yyin    = str;
+    yydebug = debug;
+    yyparse();
+    fclose(str);
+    return 0;
+}
+
+static const char *dmxConfigMatch(const char *target, DMXConfigEntryPtr entry)
+{
+    DMXConfigVirtualPtr v     = entry->virtual;
+    const char          *name = NULL;
+
+    if (v && v->name) name = v->name;
+
+    if (v && !dmxConfigCmd.config) return v->name ? v->name : "<noname>";
+    if (!name)                     return NULL;
+    if (!strcmp(name, target))     return name;
+    return NULL;
+}
+
+static DMXScreenInfo *dmxConfigAddDisplay(const char *name,
+                                          int scrnWidth,   int scrnHeight,
+                                          int scrnX,       int scrnY,
+                                          int scrnXSign,   int scrnYSign,
+                                          int rootWidth,   int rootHeight,
+                                          int rootX,       int rootY,
+                                          int rootXSign,   int rootYSign)
+{
+    DMXScreenInfo *dmxScreen;
+    
+    if (!(dmxScreens = realloc(dmxScreens,
+                               (dmxNumScreens+1) * sizeof(*dmxScreens))))
+        dmxLog(dmxFatal,
+               "dmxConfigAddDisplay: realloc failed for screen %d (%s)\n",
+               dmxNumScreens, name);
+    
+    dmxScreen = &dmxScreens[dmxNumScreens];
+    memset(dmxScreen, 0, sizeof(*dmxScreen));
+    dmxScreen->name       = name;
+    dmxScreen->index      = dmxNumScreens;
+    dmxScreen->scrnWidth  = scrnWidth;
+    dmxScreen->scrnHeight = scrnHeight;
+    dmxScreen->scrnX      = scrnX;
+    dmxScreen->scrnY      = scrnY;
+    dmxScreen->scrnXSign  = scrnXSign;
+    dmxScreen->scrnYSign  = scrnYSign;
+    dmxScreen->rootWidth  = rootWidth;
+    dmxScreen->rootHeight = rootHeight;
+    dmxScreen->rootX      = rootX;
+    dmxScreen->rootY      = rootY;
+    dmxScreen->stat       = dmxStatAlloc();
+    ++dmxNumScreens;
+    return dmxScreen;
+}
+
+DMXInputInfo *dmxConfigAddInput(const char *name, int core)
+{
+    DMXInputInfo *dmxInput;
+
+    if (!(dmxInputs = realloc(dmxInputs,
+                              (dmxNumInputs+1) * sizeof(*dmxInputs))))
+        dmxLog(dmxFatal,
+               "dmxConfigAddInput: realloc failed for input %d (%s)\n",
+               dmxNumInputs, name);
+
+    dmxInput = &dmxInputs[dmxNumInputs];
+
+    memset(dmxInput, 0, sizeof(*dmxInput));
+    dmxInput->name     = name;
+    dmxInput->inputIdx = dmxNumInputs;
+    dmxInput->scrnIdx  = -1;
+    dmxInput->core     = core;
+    ++dmxNumInputs;
+    return dmxInput;
+}
+
+static void dmxConfigCopyFromDisplay(DMXConfigDisplayPtr d)
+{
+    DMXScreenInfo *dmxScreen;
+
+    dmxScreen         = dmxConfigAddDisplay(d->name,
+                                            d->scrnWidth, d->scrnHeight,
+                                            d->scrnX,     d->scrnY,
+                                            d->scrnXSign, d->scrnYSign,
+                                            d->rootWidth, d->rootHeight,
+                                            d->rootX,     d->rootY,
+                                            d->rootXSign, d->rootXSign);
+    dmxScreen->where  = PosAbsolute;
+    dmxScreen->whereX = d->rootXOrigin;
+    dmxScreen->whereY = d->rootYOrigin;
+}
+
+static void dmxConfigCopyFromWall(DMXConfigWallPtr w)
+{
+    DMXConfigStringPtr pt;
+    DMXScreenInfo      *dmxScreen;
+    int                edge = dmxNumScreens;
+    int                last = dmxNumScreens;
+
+    if (!w->xwall && !w->ywall) { /* Try to make it square */
+        int count;
+        for (pt = w->nameList, count = 0; pt; pt = pt->next) ++count;
+        w->xwall = sqrt(count) + .5;
+    }
+
+    for (pt = w->nameList; pt; pt = pt->next) {
+        dmxScreen = dmxConfigAddDisplay(pt->string, w->width, w->height,
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        if (pt == w->nameList) { /* Upper left */
+            dmxScreen->where  = PosAbsolute;
+            dmxScreen->whereX = 0;
+            dmxScreen->whereY = 0;
+        } else if (w->xwall) {  /* Tile left to right, then top to bottom */
+            if (!((dmxNumScreens-1) % w->xwall)) {
+                dmxScreen->where          = PosBelow;
+                dmxScreen->whereRefScreen = edge;
+                edge                      = dmxNumScreens-1;
+            } else {
+                dmxScreen->where          = PosRightOf;
+                dmxScreen->whereRefScreen = last;
+            }
+        } else {                /* Tile top to bottom, then left to right */
+            if (!((dmxNumScreens-1) % w->ywall)) {
+                dmxScreen->where          = PosRightOf;
+                dmxScreen->whereRefScreen = edge;
+                edge                      = dmxNumScreens-1;
+            } else {
+                dmxScreen->where          = PosBelow;
+                dmxScreen->whereRefScreen = last;
+            }
+
+        }
+        last = dmxNumScreens-1;
+        if (dmxScreen->where == PosAbsolute)
+            dmxLog(dmxInfo, "Added %s at %d %d\n",
+                   pt->string, dmxScreen->whereX, dmxScreen->whereY);
+        else
+            dmxLog(dmxInfo, "Added %s %s %s\n",
+                   pt->string,
+                   dmxScreen->where == PosBelow ? "below" : "right of",
+                   dmxScreens[dmxScreen->whereRefScreen].name);
+    }
+}
+
+static void dmxConfigCopyFromOption(DMXConfigOptionPtr o)
+{
+    DMXConfigStringPtr pt;
+    int                argc   = 0;
+    char               **argv = NULL;
+
+    if (serverGeneration != 1) return; /* FIXME: only do once, for now */
+    if (!o || !o->string) return;
+    for (pt = o->option; pt; pt = pt->next) {
+        if (pt->string) {
+            ++argc;
+            argv = realloc(argv, (argc+1) * sizeof(*argv));
+            argv[argc] = (char *)pt->string;
+        }
+    }
+    argv[0] = NULL;
+    ProcessCommandLine(argc+1, argv);
+    free(argv);
+}
+
+static void dmxConfigCopyFromParam(DMXConfigParamPtr p)
+{
+    const char **argv;
+    int        argc;
+    
+    if ((argv = dmxConfigLookupParam(p, "xkbrules", &argc)) && argc == 2) {
+        dmxConfigSetXkbRules(argv[1]);
+    } else if ((argv = dmxConfigLookupParam(p, "xkbmodel", &argc))
+               && argc == 2) {
+        dmxConfigSetXkbModel(argv[1]);
+    } else if ((argv = dmxConfigLookupParam(p, "xkblayout", &argc))
+               && argc == 2) {
+        dmxConfigSetXkbLayout(argv[1]);
+    } else if ((argv = dmxConfigLookupParam(p, "xkbvariant", &argc))
+               && argc == 2) {
+        dmxConfigSetXkbVariant(argv[1]);
+    } else if ((argv = dmxConfigLookupParam(p, "xkboptions", &argc))
+               && argc == 2) {
+        dmxConfigSetXkbOptions(argv[1]);
+    }
+}
+
+static void dmxConfigCopyData(DMXConfigVirtualPtr v)
+{
+    DMXConfigSubPtr sub;
+    
+    if (v->dim) dmxSetWidthHeight(v->dim->x, v->dim->y);
+    else        dmxSetWidthHeight(0, 0);
+    for (sub = v->subentry; sub; sub = sub->next) {
+        switch (sub->type) {
+        case dmxConfigDisplay: dmxConfigCopyFromDisplay(sub->display); break;
+        case dmxConfigWall:    dmxConfigCopyFromWall(sub->wall);       break;
+        case dmxConfigOption:  dmxConfigCopyFromOption(sub->option);   break;
+        case dmxConfigParam:   dmxConfigCopyFromParam(sub->param);     break;
+        default:
+            dmxLog(dmxFatal,
+                   "dmxConfigCopyData: not a display, wall, or value\n");
+        }
+    }
+}
+
+static void dmxConfigFromCommandLine(void)
+{
+    DMXConfigListPtr pt;
+    
+    dmxLog(dmxInfo, "Using configuration from command line\n");
+    for (pt = dmxConfigCmd.displays; pt; pt = pt->next) {
+        DMXScreenInfo *dmxScreen = dmxConfigAddDisplay(pt->name,
+                                                       0, 0, 0, 0, 0, 0,
+                                                       0, 0, 0, 0, 0, 0);
+        if (dmxNumScreens == 1) {
+            dmxScreen->where  = PosAbsolute;
+            dmxScreen->whereX = 0;
+            dmxScreen->whereY = 0;
+            dmxLog(dmxInfo, "Added %s at %d %d\n",
+                   dmxScreen->name, dmxScreen->whereX, dmxScreen->whereY);
+        } else {
+            dmxScreen->where          = PosRightOf;
+            dmxScreen->whereRefScreen = dmxNumScreens - 2;
+            if (dmxScreen->whereRefScreen < 0) dmxScreen->whereRefScreen = 0;
+            dmxLog(dmxInfo, "Added %s %s %s\n",
+                   dmxScreen->name,
+                   dmxScreen->where == PosBelow ? "below" : "right of",
+                   dmxScreens[dmxScreen->whereRefScreen].name);
+        }
+    }
+}
+
+static void dmxConfigFromConfigFile(void)
+{
+    DMXConfigEntryPtr pt;
+    const char        *name;
+
+    for (pt = dmxConfigEntry; pt; pt = pt->next) {
+                                /* FIXME -- if an input is specified, use it */
+        if (pt->type != dmxConfigVirtual) continue;
+        if ((name = dmxConfigMatch(dmxConfigCmd.config, pt))) {
+            dmxLog(dmxInfo, "Using configuration \"%s\"\n", name);
+            dmxConfigCopyData(pt->virtual);
+            return;
+        }
+    }
+    dmxLog(dmxFatal, "Could not find configuration \"%s\" in \"%s\"\n",
+           dmxConfigCmd.config, dmxConfigCmd.filename);
+}
+
+static void dmxConfigConfigInputs(void)
+{
+    DMXConfigListPtr pt;
+
+    if (dmxNumInputs) return;
+    
+    if (dmxConfigCmd.inputs) {   /* Use command line */
+        for (pt = dmxConfigCmd.inputs; pt; pt = pt->next)
+            dmxConfigAddInput(pt->name, TRUE);
+    } else if (dmxNumScreens) { /* Use first display */
+        dmxConfigAddInput(dmxScreens[0].name, TRUE);
+    } else {                     /* Use dummy */
+        dmxConfigAddInput("dummy", TRUE);
+    }
+
+    if (dmxConfigCmd.xinputs) {  /* Non-core devices from command line */
+        for (pt = dmxConfigCmd.xinputs; pt; pt = pt->next)
+            dmxConfigAddInput(pt->name, FALSE);
+    }
+}
+
+/** Set up the appropriate global variables so that the DMX server will
+ * be initialized using the configuration specified in the config file
+ * and on the command line. */
+void dmxConfigConfigure(void)
+{
+    if (dmxConfigEntry) {
+        dmxConfigFreeEntry(dmxConfigEntry);
+        dmxConfigEntry = NULL;
+    }
+    if (dmxConfigCmd.filename) {
+        if (dmxConfigCmd.displays)
+            dmxLog(dmxWarning,
+                   "Using configuration file \"%s\" instead of command line\n",
+                   dmxConfigCmd.filename);
+        dmxConfigReadFile(dmxConfigCmd.filename, 0);
+        dmxConfigFromConfigFile();
+    } else {
+        if (dmxConfigCmd.config)
+            dmxLog(dmxWarning,
+                   "Configuration name (%s) without configuration file\n",
+                   dmxConfigCmd.config);
+        dmxConfigFromCommandLine();
+    }
+    dmxConfigConfigInputs();
+}
+
+/** This function determines the number of displays we WILL have and
+ * sets MAXSCREENS to that value.  This is difficult since the number
+ * depends on the command line (which is easy to count) or on the config
+ * file, which has to be parsed. */
+void dmxConfigSetMaxScreens(void)
+{
+    static int processing = 0;
+
+    if (processing) return;     /* Prevent reentry via ProcessCommandLine */
+    processing = 1;
+    if (dmxConfigCmd.filename) {
+        if (!dmxNumScreens)
+            dmxConfigConfigure();
+#ifndef MAXSCREENS
+        SetMaxScreens(dmxNumScreens);
+#endif
+    } else
+#ifndef MAXSCREENS
+        SetMaxScreens(dmxDisplaysFromCommandLine);
+#endif
+    processing = 0;
+}
+
+/** This macro is used to generate the following access methods:
+ * - dmxConfig{Set,Get}rules
+ * - dmxConfig{Set,Get}model
+ * - dmxConfig{Set,Get}layout
+ * - dmxConfig{Set,Get}variant
+ * - dmxConfig{Set,Get}options
+ * These methods are used to read and write information about the keyboard. */
+
+#define GEN(param,glob,def)                                                   \
+ void dmxConfigSet##glob(const char *param) {                                 \
+     if (dmx##glob) free((void *)dmx##glob);                                  \
+     dmx##glob = strdup(param);                                               \
+ }                                                                            \
+ char *dmxConfigGet##glob(void) {                                             \
+     return (char *)(dmx##glob ? dmx##glob : def);                            \
+ }
+
+GEN(rules,   XkbRules,   DMX_DEFAULT_XKB_RULES)
+GEN(model,   XkbModel,   DMX_DEFAULT_XKB_MODEL)
+GEN(layout,  XkbLayout,  DMX_DEFAULT_XKB_LAYOUT)
+GEN(variant, XkbVariant, DMX_DEFAULT_XKB_VARIANT)
+GEN(options, XkbOptions, DMX_DEFAULT_XKB_OPTIONS)
+    
diff --git a/hw/dmx/config/dmxconfig.h b/hw/dmx/config/dmxconfig.h
new file mode 100644
index 000000000..b5c0c1f1c
--- /dev/null
+++ b/hw/dmx/config/dmxconfig.h
@@ -0,0 +1,65 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ * Interface for DMX configuration file support.  \see dmxconfig.c */
+
+#ifndef _DMXCONFIG_H_
+#define _DMXCONFIG_H_
+#define DMX_DEFAULT_XKB_RULES  "xfree86"
+#define DMX_DEFAULT_XKB_MODEL  "pc101"
+#define DMX_DEFAULT_XKB_LAYOUT "us"
+#define DMX_DEFAULT_XKB_VARIANT NULL
+#define DMX_DEFAULT_XKB_OPTIONS NULL
+
+extern void dmxConfigStoreDisplay(const char *display);
+extern void dmxConfigStoreInput(const char *input); /* Core devices */
+extern void dmxConfigStoreXInput(const char *input); /* Non-core devices */
+extern void dmxConfigStoreFile(const char *file);
+extern void dmxConfigStoreConfig(const char *config);
+extern void dmxConfigConfigure(void);
+extern void dmxConfigSetMaxScreens(void);
+
+extern void dmxConfigSetXkbRules(const char *rules);
+extern void dmxConfigSetXkbModel(const char *model);
+extern void dmxConfigSetXkbLayout(const char *layout);
+extern void dmxConfigSetXkbVariant(const char *variant);
+extern void dmxConfigSetXkbOptions(const char *options);
+
+extern char *dmxConfigGetXkbRules(void);
+extern char *dmxConfigGetXkbModel(void);
+extern char *dmxConfigGetXkbLayout(void);
+extern char *dmxConfigGetXkbVariant(void);
+extern char *dmxConfigGetXkbOptions(void);
+#endif
diff --git a/hw/dmx/config/dmxparse.c b/hw/dmx/config/dmxparse.c
new file mode 100644
index 000000000..eed59cf57
--- /dev/null
+++ b/hw/dmx/config/dmxparse.c
@@ -0,0 +1,607 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ *
+ * This file provides support routines and helper functions to be used
+ * by the DMX configuration file parser.
+ *
+ * Because the DMX configuration file parsing should be capable of being
+ * used in a stand-alone fashion (i.e., independent from the DMX server
+ * source tree), no dependencies on other DMX routines are made. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "dmxparse.h"
+
+/** A general error logging routine that does not depend on the dmxLog
+ * functions. */
+void dmxConfigLog(const char *format, ...)
+{
+    va_list args;
+    
+    va_start(args, format);
+    vprintf(format, args);      /* RATS: All calls to dmxConfigLog from
+                                 * dmxparse.c and dmxprint.c use a
+                                 * trusted format. */
+    va_end(args);
+}
+
+void *dmxConfigAlloc(unsigned long bytes)
+{
+    void *area = malloc(bytes);
+    if (!area) {
+        dmxConfigLog("dmxConfigAlloc: out of memory\n");
+        return NULL;
+    }
+    memset(area, 0, bytes);
+    return area;
+}
+
+void *dmxConfigRealloc(void *orig, unsigned long orig_bytes,
+                       unsigned long bytes)
+{
+    unsigned char *area = realloc(orig, bytes);
+    if (!area) {
+        dmxConfigLog("dmxConfigRealloc: out of memory\n");
+        return NULL;
+    }
+    memset(area + orig_bytes, 0, bytes - orig_bytes);
+    return area;
+}
+
+const char *dmxConfigCopyString(const char *string, int length)
+{
+    char *copy;
+    
+    if (!length) length = strlen(string);
+    copy = dmxConfigAlloc(length + 1);
+    if (length) strncpy(copy, string, length);
+    copy[length] = '\0';
+    return copy;
+}
+
+void dmxConfigFree(void *area)
+{
+    if (area) free(area);
+}
+
+DMXConfigTokenPtr dmxConfigCreateToken(int token, int line,
+                                       const char *comment)
+{
+    DMXConfigTokenPtr pToken = dmxConfigAlloc(sizeof(*pToken));
+    pToken->token   = token;
+    pToken->line    = line;
+    pToken->comment = comment;
+    return pToken;
+}
+
+void dmxConfigFreeToken(DMXConfigTokenPtr p)
+{
+    if (!p) return;
+    dmxConfigFree((void *)p->comment);
+    dmxConfigFree(p);
+}
+
+DMXConfigStringPtr dmxConfigCreateString(int token, int line,
+                                         const char *comment,
+                                         const char *string)
+{
+    DMXConfigStringPtr pString = dmxConfigAlloc(sizeof(*pString));
+
+    pString->token   = token;
+    pString->line    = line;
+    pString->comment = comment;
+    pString->string  = string;
+    return pString;
+}
+
+void dmxConfigFreeString(DMXConfigStringPtr p)
+{
+    DMXConfigStringPtr next;
+
+    if (!p) return;
+    do {
+        next = p->next;
+        dmxConfigFree((void *)p->comment);
+        dmxConfigFree((void *)p->string);
+        dmxConfigFree(p);
+    } while ((p = next));
+}
+ 
+DMXConfigNumberPtr dmxConfigCreateNumber(int token, int line,
+                                         const char *comment,
+                                         int number)
+{
+    DMXConfigNumberPtr pNumber = dmxConfigAlloc(sizeof(*pNumber));
+
+    pNumber->token   = token;
+    pNumber->line    = line;
+    pNumber->comment = comment;
+    pNumber->number  = number;
+    return pNumber;
+}
+
+void dmxConfigFreeNumber(DMXConfigNumberPtr p)
+{
+    if (!p) return;
+    dmxConfigFree((void *)p->comment);
+    dmxConfigFree(p);
+}
+
+DMXConfigPairPtr dmxConfigCreatePair(int token, int line,
+                                     const char *comment,
+                                     int x, int y,
+                                     int xsign, int ysign)
+{
+    DMXConfigPairPtr pPair = dmxConfigAlloc(sizeof(*pPair));
+
+    pPair->token   = token;
+    pPair->line    = line;
+    pPair->comment = comment;
+    pPair->x       = x;
+    pPair->y       = y;
+    pPair->xsign   = (xsign < 0) ? -1 : 1;
+    pPair->ysign   = (ysign < 0) ? -1 : 1;
+    return pPair;
+}
+
+void dmxConfigFreePair(DMXConfigPairPtr p)
+{
+    if (!p) return;
+    dmxConfigFree((void *)p->comment);
+    dmxConfigFree(p);
+}
+
+DMXConfigCommentPtr dmxConfigCreateComment(int token, int line,
+                                           const char *comment)
+{
+    DMXConfigCommentPtr pComment = dmxConfigAlloc(sizeof(*pComment));
+
+    pComment->token   = token;
+    pComment->line    = line;
+    pComment->comment = comment;
+    return pComment;
+}
+
+void dmxConfigFreeComment(DMXConfigCommentPtr p)
+{
+    if (!p) return;
+    dmxConfigFree((void *)p->comment);
+    dmxConfigFree(p);
+}
+
+DMXConfigPartDimPtr dmxConfigCreatePartDim(DMXConfigPairPtr pDim,
+                                           DMXConfigPairPtr pOffset)
+{
+    DMXConfigPartDimPtr pPart = dmxConfigAlloc(sizeof(*pPart));
+    pPart->dim    = pDim;
+    pPart->offset = pOffset;
+    return pPart;
+}
+
+void dmxConfigFreePartDim(DMXConfigPartDimPtr p)
+{
+    if (!p) return;
+    dmxConfigFreePair(p->dim);
+    dmxConfigFreePair(p->offset);
+    dmxConfigFree(p);
+}
+
+DMXConfigFullDimPtr dmxConfigCreateFullDim(DMXConfigPartDimPtr pScrn,
+                                           DMXConfigPartDimPtr pRoot)
+{
+    DMXConfigFullDimPtr pFull = dmxConfigAlloc(sizeof(*pFull));
+    pFull->scrn = pScrn;
+    pFull->root = pRoot;
+    return pFull;
+}
+
+void dmxConfigFreeFullDim(DMXConfigFullDimPtr p)
+{
+    if (!p) return;
+    dmxConfigFreePartDim(p->scrn);
+    dmxConfigFreePartDim(p->root);
+    dmxConfigFree(p);
+}
+
+DMXConfigDisplayPtr dmxConfigCreateDisplay(DMXConfigTokenPtr pStart,
+                                           DMXConfigStringPtr pName,
+                                           DMXConfigFullDimPtr pDim,
+                                           DMXConfigPairPtr pOrigin,
+                                           DMXConfigTokenPtr pEnd)
+{
+    DMXConfigDisplayPtr pDisplay = dmxConfigAlloc(sizeof(*pDisplay));
+
+    memset(pDisplay, 0, sizeof(*pDisplay));
+
+    pDisplay->start          = pStart;
+    pDisplay->dname          = pName;
+    pDisplay->dim            = pDim;
+    pDisplay->origin         = pOrigin;
+    pDisplay->end            = pEnd;
+
+    pDisplay->name           = pName ? pName->string : NULL;
+    pDisplay->rootXOrigin    = pOrigin ? pOrigin->x : 0;
+    pDisplay->rootYOrigin    = pOrigin ? pOrigin->y : 0;
+
+    if (pDim && pDim->scrn && pDim->scrn->dim) {
+        pDisplay->scrnWidth  = pDim->scrn->dim->x;
+        pDisplay->scrnHeight = pDim->scrn->dim->y;
+    }
+    if (pDim && pDim->scrn && pDim->scrn->offset) {
+        pDisplay->scrnX      = pDim->scrn->offset->x;
+        pDisplay->scrnY      = pDim->scrn->offset->y;
+        pDisplay->scrnXSign  = pDim->scrn->offset->xsign;
+        pDisplay->scrnYSign  = pDim->scrn->offset->ysign;
+    }
+    
+    if (pDim && pDim->root) {
+        if (pDim->root->dim) {
+            pDisplay->rootWidth  = pDim->root->dim->x;
+            pDisplay->rootHeight = pDim->root->dim->y;
+        }
+        if (pDim->root->offset) {
+            pDisplay->rootX      = pDim->root->offset->x;
+            pDisplay->rootY      = pDim->root->offset->y;
+            pDisplay->rootXSign  = pDim->root->offset->xsign;
+            pDisplay->rootYSign  = pDim->root->offset->ysign;
+        }
+    } else {                    /* If no root specification, copy width
+                                 * and height from scrn -- leave offset
+                                 * as zero, since it is relative to
+                                 * scrn. */
+        pDisplay->rootWidth  = pDisplay->scrnWidth;
+        pDisplay->rootHeight = pDisplay->scrnHeight;
+    }
+
+
+    return pDisplay;
+}
+
+void dmxConfigFreeDisplay(DMXConfigDisplayPtr p)
+{
+    if (!p) return;
+    dmxConfigFreeToken(p->start);
+    dmxConfigFreeString(p->dname);
+    dmxConfigFreeFullDim(p->dim);
+    dmxConfigFreeToken(p->end);
+    dmxConfigFree(p);
+}
+
+DMXConfigWallPtr dmxConfigCreateWall(DMXConfigTokenPtr pStart,
+                                     DMXConfigPairPtr pWallDim,
+                                     DMXConfigPairPtr pDisplayDim,
+                                     DMXConfigStringPtr pNameList,
+                                     DMXConfigTokenPtr pEnd)
+{
+    DMXConfigWallPtr pWall = dmxConfigAlloc(sizeof(*pWall));
+
+    pWall->start      = pStart;
+    pWall->wallDim    = pWallDim;
+    pWall->displayDim = pDisplayDim;
+    pWall->nameList   = pNameList;
+    pWall->end        = pEnd;
+
+    pWall->width      = pDisplayDim ? pDisplayDim->x : 0;
+    pWall->height     = pDisplayDim ? pDisplayDim->y : 0;
+    pWall->xwall      = pWallDim    ? pWallDim->x    : 0;
+    pWall->ywall      = pWallDim    ? pWallDim->y    : 0;
+
+    return pWall;
+}
+
+void dmxConfigFreeWall(DMXConfigWallPtr p)
+{
+    if (!p) return;
+    dmxConfigFreeToken(p->start);
+    dmxConfigFreePair(p->wallDim);
+    dmxConfigFreePair(p->displayDim);
+    dmxConfigFreeString(p->nameList);
+    dmxConfigFreeToken(p->end);
+    dmxConfigFree(p);
+}
+
+DMXConfigOptionPtr dmxConfigCreateOption(DMXConfigTokenPtr pStart,
+                                         DMXConfigStringPtr pOption,
+                                         DMXConfigTokenPtr pEnd)
+{
+    int                length = 0;
+    int                offset = 0;
+    DMXConfigStringPtr p;
+    DMXConfigOptionPtr option = dmxConfigAlloc(sizeof(*option));
+
+    for (p = pOption; p; p = p->next) {
+        if (p->string) length += strlen(p->string) + 1;
+    }
+
+    option->string = dmxConfigAlloc(length + 1);
+    
+    for (p = pOption; p; p = p->next) {
+        if (p->string) {
+            int len = strlen(p->string);
+            strncpy(option->string + offset, p->string, len);
+            offset += len;
+            if (p->next) option->string[offset++] = ' ';
+        }
+    }
+    option->string[offset] = '\0';
+
+    option->start  = pStart;
+    option->option = pOption;
+    option->end    = pEnd;
+
+    return option;
+}
+
+void dmxConfigFreeOption(DMXConfigOptionPtr p)
+{
+    if (!p) return;
+    if (p->string) free(p->string);
+    dmxConfigFreeToken(p->start);
+    dmxConfigFreeString(p->option);
+    dmxConfigFreeToken(p->end);
+    dmxConfigFree(p);
+}
+
+const char **dmxConfigLookupParam(DMXConfigParamPtr p, const char *key,
+                                  int *argc)
+{
+    DMXConfigParamPtr pt;
+
+    for (pt = p; pt; pt = pt->next) {
+        if (pt->argv && !strcasecmp(pt->argv[0], key)) {
+            *argc = pt->argc;
+            return pt->argv;
+        }
+    }
+    *argc  = 0;
+    return NULL;
+}
+
+DMXConfigParamPtr dmxConfigCreateParam(DMXConfigTokenPtr pStart,
+                                       DMXConfigTokenPtr pOpen,
+                                       DMXConfigStringPtr pParam,
+                                       DMXConfigTokenPtr pClose,
+                                       DMXConfigTokenPtr pEnd)
+{
+    DMXConfigParamPtr  param = dmxConfigAlloc(sizeof(*param));
+    DMXConfigStringPtr pt;
+
+    param->argc = 0;
+    param->argv = NULL;
+    for (pt = pParam; pt; pt = pt->next) {
+        if (pt->string) {
+            param->argv = realloc(param->argv,
+                                  (param->argc+2) * sizeof(*param->argv));
+            param->argv[param->argc] = pt->string;
+            ++param->argc;
+        }
+    }
+    if (param->argv) param->argv[param->argc] = NULL;
+
+    param->start = pStart;
+    param->open  = pOpen;
+    param->param = pParam;
+    param->close = pClose;
+    param->end   = pEnd;
+
+    return param;
+}
+
+void dmxConfigFreeParam(DMXConfigParamPtr p)
+{
+    DMXConfigParamPtr next;
+
+    if (!p) return;
+    do {
+        next = p->next;
+        dmxConfigFreeToken(p->start);
+        dmxConfigFreeToken(p->open);
+        dmxConfigFreeString(p->param);
+        dmxConfigFreeToken(p->close);
+        dmxConfigFreeToken(p->end);
+        dmxConfigFree(p->argv);
+        dmxConfigFree(p);
+    } while ((p = next));
+}
+
+DMXConfigSubPtr dmxConfigCreateSub(DMXConfigType type,
+                                   DMXConfigCommentPtr comment,
+                                   DMXConfigDisplayPtr display,
+                                   DMXConfigWallPtr wall,
+                                   DMXConfigOptionPtr option,
+                                   DMXConfigParamPtr param)
+{
+    DMXConfigSubPtr pSub = dmxConfigAlloc(sizeof(*pSub));
+    pSub->type = type;
+    switch (type) {
+    case dmxConfigComment: pSub->comment = comment;                     break;
+    case dmxConfigDisplay: pSub->display = display;                     break;
+    case dmxConfigWall:    pSub->wall    = wall;                        break;
+    case dmxConfigOption:  pSub->option  = option;                      break;
+    case dmxConfigParam:   pSub->param   = param;                       break;
+    default: dmxConfigLog("Type %d not supported in subentry\n", type); break;
+    }
+    return pSub;
+}
+
+void dmxConfigFreeSub(DMXConfigSubPtr sub)
+{
+    DMXConfigSubPtr pt;
+
+    for (pt = sub; pt; pt = pt->next) {
+        switch (pt->type) {
+        case dmxConfigComment: dmxConfigFreeComment(pt->comment); break;
+        case dmxConfigDisplay: dmxConfigFreeDisplay(pt->display); break;
+        case dmxConfigWall:    dmxConfigFreeWall(pt->wall);       break;
+        case dmxConfigOption:  dmxConfigFreeOption(pt->option);   break;
+        case dmxConfigParam:   dmxConfigFreeParam(pt->param);     break;
+        default:
+            dmxConfigLog("Type %d not supported in subentry\n", pt->type);
+            break;
+        }
+    }
+    dmxConfigFree(sub);
+}
+
+DMXConfigSubPtr dmxConfigSubComment(DMXConfigCommentPtr comment)
+{
+    return dmxConfigCreateSub(dmxConfigComment, comment, NULL, NULL, NULL,
+                              NULL);
+}
+
+DMXConfigSubPtr dmxConfigSubDisplay(DMXConfigDisplayPtr display)
+{
+    return dmxConfigCreateSub(dmxConfigDisplay, NULL, display, NULL, NULL,
+                              NULL);
+}
+
+DMXConfigSubPtr dmxConfigSubWall(DMXConfigWallPtr wall)
+{
+    return dmxConfigCreateSub(dmxConfigWall, NULL, NULL, wall, NULL, NULL);
+}
+
+DMXConfigSubPtr dmxConfigSubOption(DMXConfigOptionPtr option)
+{
+    return dmxConfigCreateSub(dmxConfigOption, NULL, NULL, NULL, option, NULL);
+}
+
+DMXConfigSubPtr dmxConfigSubParam(DMXConfigParamPtr param)
+{
+    return dmxConfigCreateSub(dmxConfigParam, NULL, NULL, NULL, NULL, param);
+}
+
+extern DMXConfigSubPtr dmxConfigAddSub(DMXConfigSubPtr head,
+                                       DMXConfigSubPtr sub)
+{
+    DMXConfigSubPtr pt;
+    
+    if (!head) return sub;
+    for (pt = head; pt->next; pt = pt->next);
+    pt->next = sub;
+    return head;
+}
+
+DMXConfigVirtualPtr dmxConfigCreateVirtual(DMXConfigTokenPtr pStart,
+                                           DMXConfigStringPtr pName,
+                                           DMXConfigPairPtr pDim,
+                                           DMXConfigTokenPtr pOpen,
+                                           DMXConfigSubPtr pSubentry,
+                                           DMXConfigTokenPtr pClose)
+{
+    DMXConfigVirtualPtr pVirtual = dmxConfigAlloc(sizeof(*pVirtual));
+
+    pVirtual->start    = pStart;
+    pVirtual->vname    = pName;
+    pVirtual->dim      = pDim;
+    pVirtual->open     = pOpen;
+    pVirtual->subentry = pSubentry;
+    pVirtual->close    = pClose;
+
+    pVirtual->name     = pName ? pName->string : NULL;
+    pVirtual->width    = pDim ? pDim->x : 0;
+    pVirtual->height   = pDim ? pDim->y : 0;
+    
+    return pVirtual;
+}
+
+void dmxConfigFreeVirtual(DMXConfigVirtualPtr virtual)
+{
+    dmxConfigFreeToken(virtual->start);
+    dmxConfigFreeString(virtual->vname);
+    dmxConfigFreePair(virtual->dim);
+    dmxConfigFreeToken(virtual->open);
+    dmxConfigFreeSub(virtual->subentry);
+    dmxConfigFreeToken(virtual->close);
+    dmxConfigFree(virtual);
+}
+
+DMXConfigEntryPtr dmxConfigCreateEntry(DMXConfigType type,
+                                       DMXConfigCommentPtr comment,
+                                       DMXConfigVirtualPtr virtual)
+{
+    DMXConfigEntryPtr pEntry = dmxConfigAlloc(sizeof(*pEntry));
+    pEntry->type = type;
+    switch (type) {
+    case dmxConfigComment: pEntry->comment = comment;                break;
+    case dmxConfigVirtual: pEntry->virtual = virtual;                break;
+    default: dmxConfigLog("Type %d not supported in entry\n", type); break;
+    }
+    return pEntry;
+}
+
+void dmxConfigFreeEntry(DMXConfigEntryPtr entry)
+{
+    DMXConfigEntryPtr pt;
+
+    for (pt = entry; pt; pt = pt->next) {
+        switch (pt->type) {
+        case dmxConfigComment: dmxConfigFreeComment(pt->comment); break;
+        case dmxConfigVirtual: dmxConfigFreeVirtual(pt->virtual); break;
+        default:
+            dmxConfigLog("Type %d not supported in entry\n", pt->type);
+            break;
+        }
+    }
+    dmxConfigFree(entry);
+}
+
+DMXConfigEntryPtr dmxConfigAddEntry(DMXConfigEntryPtr head,
+                                    DMXConfigType type,
+                                    DMXConfigCommentPtr comment,
+                                    DMXConfigVirtualPtr virtual)
+{
+    DMXConfigEntryPtr child = dmxConfigCreateEntry(type, comment, virtual);
+    DMXConfigEntryPtr pt;
+
+    if (!head) return child;
+
+    for (pt = head; pt->next; pt = pt->next);
+    pt->next = child;
+
+    return head;
+}
+
+DMXConfigEntryPtr dmxConfigEntryComment(DMXConfigCommentPtr comment)
+{
+    return dmxConfigCreateEntry(dmxConfigComment, comment, NULL);
+}
+
+DMXConfigEntryPtr dmxConfigEntryVirtual(DMXConfigVirtualPtr virtual)
+{
+    return dmxConfigCreateEntry(dmxConfigVirtual, NULL, virtual);
+}
diff --git a/hw/dmx/config/dmxparse.h b/hw/dmx/config/dmxparse.h
new file mode 100644
index 000000000..8ea84f4e3
--- /dev/null
+++ b/hw/dmx/config/dmxparse.h
@@ -0,0 +1,298 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ * Interface to DMX configuration file parser.  \see dmxparse.c */
+
+#ifndef _DMXPARSE_H_
+#define _DMXPARSE_H_
+
+#include <stdio.h>              /* For FILE */
+
+/** Stores tokens not stored in other structures (e.g., keywords and ;) */
+typedef struct _DMXConfigToken {
+    int                      token;
+    int                      line;
+    const char               *comment;
+} DMXConfigToken, *DMXConfigTokenPtr;
+
+/** Stores parsed strings. */
+typedef struct _DMXConfigString {
+    int                      token;
+    int                      line;
+    const char               *comment;
+    const char               *string;
+    struct _DMXConfigString  *next;
+} DMXConfigString, *DMXConfigStringPtr;
+
+/** Stores parsed numbers. */
+typedef struct _DMXConfigNumber {
+    int                      token;
+    int                      line;
+    const char               *comment;
+    int                      number;
+} DMXConfigNumber, *DMXConfigNumberPtr;
+
+/** Stores parsed pairs (e.g., x y) */
+typedef struct _DMXConfigPair {
+    int                      token;
+    int                      line;
+    const char               *comment;
+    int                      x;
+    int                      y;
+    int                      xsign;
+    int                      ysign;
+} DMXConfigPair, *DMXConfigPairPtr;
+
+/** Stores parsed comments not stored with a token. */
+typedef struct _DMXConfigComment {
+    int                      token;
+    int                      line;
+    const char               *comment;
+} DMXConfigComment, *DMXConfigCommentPtr;
+
+typedef enum {
+    dmxConfigComment,
+    dmxConfigVirtual,
+    dmxConfigDisplay,
+    dmxConfigWall,
+    dmxConfigOption,
+    dmxConfigParam
+} DMXConfigType;
+
+/** Stores a geometry specification. */
+typedef struct _DMXConfigPartDim {
+    DMXConfigPairPtr         dim;
+    DMXConfigPairPtr         offset;
+} DMXConfigPartDim, *DMXConfigPartDimPtr;
+
+/** Stores a pair of geometry specifications. */
+typedef struct _DMXConfigFullDim {
+    DMXConfigPartDimPtr      scrn;
+    DMXConfigPartDimPtr      root;
+} DMXConfigFullDim, *DMXConfigFullDimPtr;
+
+/** Stores parsed display information. */
+typedef struct _DMXConfigDisplay {
+                                /* Summary information */
+    const char               *name;
+                                /* Screen Window Geometry */
+    int                      scrnWidth, scrnHeight;
+    int                      scrnX, scrnY;
+    int                      scrnXSign, scrnYSign;
+                                /* Root Window Geometry */
+    int                      rootWidth, rootHeight;
+    int                      rootX, rootY;
+    int                      rootXSign, rootYSign;
+                                /* Origin in global space */
+    int                      rootXOrigin, rootYOrigin;
+    
+                                /* Raw configuration information */
+    DMXConfigTokenPtr        start;
+    DMXConfigStringPtr       dname;
+    DMXConfigFullDimPtr      dim;
+    DMXConfigPairPtr         origin;
+    DMXConfigTokenPtr        end;
+} DMXConfigDisplay, *DMXConfigDisplayPtr;
+
+/** Stores parsed wall information. */
+typedef struct _DMXConfigWall {
+                                /* Summary information */
+    int                      width, height; /* dimensions of displays */
+    int                      xwall, ywall; /* dimensions of wall, in tiles */
+
+    
+                                /* Raw configuration informaiton */
+    DMXConfigTokenPtr        start;
+    DMXConfigPairPtr         wallDim;
+    DMXConfigPairPtr         displayDim;
+    DMXConfigStringPtr       nameList;
+    DMXConfigTokenPtr        end;
+} DMXConfigWall, *DMXConfigWallPtr;
+
+/** Stores parsed option information. */
+typedef struct _DMXConfigOption {
+                                /* Summary information */
+    char                     *string;
+    
+                                /* Raw configuration informaiton */
+    DMXConfigTokenPtr        start;
+    DMXConfigStringPtr       option;
+    DMXConfigTokenPtr        end;
+} DMXConfigOption, *DMXConfigOptionPtr;
+
+/** Stores parsed param information. */
+typedef struct _DMXConfigParam {
+    int                      argc;
+    const char               **argv;
+    
+    DMXConfigTokenPtr        start;
+    DMXConfigTokenPtr        open;
+    DMXConfigStringPtr       param;
+    DMXConfigTokenPtr        close;
+    DMXConfigTokenPtr        end; /* Either open/close OR end */
+    struct _DMXConfigParam   *next;
+} DMXConfigParam, *DMXConfigParamPtr;
+
+/** Stores options under an entry (subentry). */
+typedef struct _DMXConfigSub {
+    DMXConfigType             type;
+    DMXConfigCommentPtr       comment;
+    DMXConfigDisplayPtr       display;
+    DMXConfigWallPtr          wall;
+    DMXConfigOptionPtr        option;
+    DMXConfigParamPtr         param;
+    struct _DMXConfigSub      *next;
+} DMXConfigSub, *DMXConfigSubPtr;
+
+/** Stores parsed virtual information. */
+typedef struct _DMXConfigVirtual {
+                                /* Summary information */
+    const char                *name;
+    int                       width, height;
+
+                                /* Raw configuration information */
+    DMXConfigTokenPtr         start;
+    DMXConfigStringPtr        vname;
+    DMXConfigPairPtr          dim;
+    DMXConfigTokenPtr         open;
+    DMXConfigSubPtr           subentry;
+    DMXConfigTokenPtr         close;
+} DMXConfigVirtual, *DMXConfigVirtualPtr;
+
+/** Heads entry storage. */
+typedef struct _DMXConfigEntry {
+    DMXConfigType            type;
+    DMXConfigCommentPtr      comment;
+    DMXConfigVirtualPtr      virtual;
+    struct _DMXConfigEntry   *next;
+} DMXConfigEntry, *DMXConfigEntryPtr;
+
+extern DMXConfigEntryPtr   dmxConfigEntry;
+
+extern int                 yylex(void);
+extern int                 yydebug;
+extern void                yyerror(const char *message);
+
+extern void                dmxConfigLog(const char *format, ...);
+extern void                *dmxConfigAlloc(unsigned long bytes);
+extern void                *dmxConfigRealloc(void *orig,
+                                             unsigned long orig_bytes,
+                                             unsigned long bytes);
+extern const char          *dmxConfigCopyString(const char *string,
+                                                int length);
+extern void                dmxConfigFree(void *area);
+extern DMXConfigTokenPtr   dmxConfigCreateToken(int token, int line,
+                                                const char *comment);
+extern void                dmxConfigFreeToken(DMXConfigTokenPtr p);
+extern DMXConfigStringPtr  dmxConfigCreateString(int token, int line,
+                                                 const char *comment,
+                                                 const char *string);
+extern void                dmxConfigFreeString(DMXConfigStringPtr p);
+extern DMXConfigNumberPtr  dmxConfigCreateNumber(int token, int line,
+                                                 const char *comment,
+                                                 int number);
+extern void                dmxConfigFreeNumber(DMXConfigNumberPtr p);
+extern DMXConfigPairPtr    dmxConfigCreatePair(int token, int line,
+                                               const char *comment,
+                                               int x, int y,
+                                               int xsign, int ysign);
+extern void                dmxConfigFreePair(DMXConfigPairPtr p);
+extern DMXConfigCommentPtr dmxConfigCreateComment(int token, int line,
+                                                  const char *comment);
+extern void                dmxConfigFreeComment(DMXConfigCommentPtr p);
+extern DMXConfigPartDimPtr dmxConfigCreatePartDim(DMXConfigPairPtr pDim,
+                                                  DMXConfigPairPtr pOffset);
+extern void                dmxConfigFreePartDim(DMXConfigPartDimPtr p);
+extern DMXConfigFullDimPtr dmxConfigCreateFullDim(DMXConfigPartDimPtr pScrn,
+                                                  DMXConfigPartDimPtr pRoot);
+extern void                dmxConfigFreeFullDim(DMXConfigFullDimPtr p);
+extern DMXConfigDisplayPtr dmxConfigCreateDisplay(DMXConfigTokenPtr pStart,
+                                                  DMXConfigStringPtr pName,
+                                                  DMXConfigFullDimPtr pDim,
+                                                  DMXConfigPairPtr pOrigin,
+                                                  DMXConfigTokenPtr pEnd);
+extern void                dmxConfigFreeDisplay(DMXConfigDisplayPtr p);
+extern DMXConfigWallPtr    dmxConfigCreateWall(DMXConfigTokenPtr pStart,
+                                               DMXConfigPairPtr pWallDim,
+                                               DMXConfigPairPtr pDisplayDim,
+                                               DMXConfigStringPtr pNameList,
+                                               DMXConfigTokenPtr pEnd);
+extern void                dmxConfigFreeWall(DMXConfigWallPtr p);
+extern DMXConfigOptionPtr  dmxConfigCreateOption(DMXConfigTokenPtr pStart,
+                                                 DMXConfigStringPtr pOption,
+                                                 DMXConfigTokenPtr pEnd);
+extern void                dmxConfigFreeOption(DMXConfigOptionPtr p);
+extern DMXConfigParamPtr   dmxConfigCreateParam(DMXConfigTokenPtr pStart,
+                                                DMXConfigTokenPtr pOpen,
+                                                DMXConfigStringPtr pParam,
+                                                DMXConfigTokenPtr pClose,
+                                                DMXConfigTokenPtr pEnd);
+extern void                dmxConfigFreeParam(DMXConfigParamPtr p);
+extern const char          **dmxConfigLookupParam(DMXConfigParamPtr p,
+                                                  const char *key,
+                                                  int *argc);
+extern DMXConfigSubPtr     dmxConfigCreateSub(DMXConfigType type,
+                                              DMXConfigCommentPtr comment,
+                                              DMXConfigDisplayPtr display,
+                                              DMXConfigWallPtr wall,
+                                              DMXConfigOptionPtr option,
+                                              DMXConfigParamPtr param);
+extern void                dmxConfigFreeSub(DMXConfigSubPtr sub);
+extern DMXConfigSubPtr     dmxConfigSubComment(DMXConfigCommentPtr comment);
+extern DMXConfigSubPtr     dmxConfigSubDisplay(DMXConfigDisplayPtr display);
+extern DMXConfigSubPtr     dmxConfigSubWall(DMXConfigWallPtr wall);
+extern DMXConfigSubPtr     dmxConfigSubOption(DMXConfigOptionPtr option);
+extern DMXConfigSubPtr     dmxConfigSubParam(DMXConfigParamPtr param);
+extern DMXConfigSubPtr     dmxConfigAddSub(DMXConfigSubPtr head,
+                                           DMXConfigSubPtr sub);
+extern DMXConfigVirtualPtr dmxConfigCreateVirtual(DMXConfigTokenPtr pStart,
+                                                  DMXConfigStringPtr pName,
+                                                  DMXConfigPairPtr pDim,
+                                                  DMXConfigTokenPtr pOpen,
+                                                  DMXConfigSubPtr pSubentry,
+                                                  DMXConfigTokenPtr pClose);
+extern void                dmxConfigFreeVirtual(DMXConfigVirtualPtr virtual);
+extern DMXConfigEntryPtr   dmxConfigCreateEntry(DMXConfigType type,
+                                                DMXConfigCommentPtr comment,
+                                                DMXConfigVirtualPtr virtual);
+extern void                dmxConfigFreeEntry(DMXConfigEntryPtr entry);
+extern DMXConfigEntryPtr   dmxConfigAddEntry(DMXConfigEntryPtr head,
+                                             DMXConfigType type,
+                                             DMXConfigCommentPtr comment,
+                                             DMXConfigVirtualPtr virtual);
+extern DMXConfigEntryPtr   dmxConfigEntryComment(DMXConfigCommentPtr comment);
+extern DMXConfigEntryPtr   dmxConfigEntryVirtual(DMXConfigVirtualPtr virtual);
+
+#endif
diff --git a/hw/dmx/config/dmxprint.c b/hw/dmx/config/dmxprint.c
new file mode 100644
index 000000000..a269fab0e
--- /dev/null
+++ b/hw/dmx/config/dmxprint.c
@@ -0,0 +1,452 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ *
+ * This file provides support routines and helper functions to be used
+ * to pretty-print DMX configurations.
+ * 
+ * Because the DMX configuration file parsing should be capable of being
+ * used in a stand-alone fashion (i.e., independent from the DMX server
+ * source tree), no dependencies on other DMX routines are made. */
+
+#include "dmxconfig.h"
+#include "dmxparse.h"
+#include "dmxprint.h"
+#include "parser.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+static FILE *str   = NULL;
+static int  indent = 0;
+static int  pos    = 0;
+
+/** Stack of indentation information used for pretty-printing
+ * configuration information. */
+static struct stack {
+    int          base;
+    int          comment;
+    int          step;
+    struct stack *next;
+} *stack, initialStack = { 0, 0, 4, NULL };
+
+static void dmxConfigIndent(void)
+{
+    int i;
+    if (indent < 0)  indent = 0;
+    if (indent > 40) indent = 40;
+    for (i = 0; i < indent; i++) fprintf(str, " ");
+}
+
+static void dmxConfigNewline(void)
+{
+    if (pos) fprintf(str, "\n");
+    pos = 0;
+}
+
+static void dmxConfigPushState(int base, int comment, int step)
+{
+    struct stack *new = dmxConfigAlloc(sizeof(*new));
+    new->base    = base;
+    new->comment = comment;
+    new->step    = step;
+    new->next    = stack;
+    stack        = new;
+    indent       = base;
+    dmxConfigNewline();
+}
+
+static void dmxConfigPushComment(void)
+{
+    if (stack) indent = stack->comment;
+}
+
+static void dmxConfigPushStep(void)
+{
+    if (stack) indent = stack->step;
+}
+
+static void dmxConfigPopState(void)
+{
+    struct stack *old = stack;
+
+    if (!stack) return;
+    indent = old->base;
+    stack  = old->next;
+    if (!stack) dmxConfigLog("Stack underflow\n");
+    dmxConfigFree(old);
+    dmxConfigNewline();
+}
+
+static void dmxConfigOutput(int addSpace, int doNewline, const char *comment,
+                            const char *format, ...)
+{
+    va_list args;
+
+    if (!pos) dmxConfigIndent();
+    else if (addSpace) fprintf(str, " ");
+
+    if (format) {
+        va_start(args, format);
+                                /* RATS: This hasn't been audited -- it
+                                 * could probably result in a buffer
+                                 * overflow. */
+        pos += vfprintf(str, format, args); /* assumes no newlines! */
+        va_end(args);
+    }
+
+    if (comment) {
+        if (pos) fprintf(str, " ");
+        pos += fprintf(str, "#%s", comment);
+        dmxConfigNewline();
+        dmxConfigPushComment();
+    } else if (doNewline) dmxConfigNewline();
+}
+
+static void dmxConfigPrintComment(DMXConfigCommentPtr p)
+{
+    dmxConfigOutput(1, 1, p->comment, NULL);
+}
+
+static void dmxConfigPrintTokenFlag(DMXConfigTokenPtr p, int flag)
+{
+    if (!p) return;
+    switch (p->token) {
+    case T_VIRTUAL:
+        dmxConfigPushState(0, 4, 4);
+        dmxConfigOutput(0, 0, p->comment, "virtual");
+        break;
+    case T_DISPLAY:
+        dmxConfigPushState(4, 12, 16);
+        dmxConfigOutput(0, 0, p->comment, "display");
+        break;
+    case T_WALL:
+        dmxConfigPushState(4, 12, 16);
+        dmxConfigOutput(0, 0, p->comment, "wall");
+        break;
+    case T_OPTION:
+        dmxConfigPushState(4, 12, 16);
+        dmxConfigOutput(0, 0, p->comment, "option");
+        break;
+    case T_PARAM:
+        dmxConfigPushState(4, 8, 12);
+        dmxConfigOutput(0, 0, p->comment, "param");
+        break;
+    case ';':
+        dmxConfigOutput(0, 1, p->comment, ";");
+        if (flag) dmxConfigPopState();
+        break;
+    case '{':
+        dmxConfigOutput(1, 1, p->comment, "{");
+        dmxConfigPushStep();
+        break;
+    case '}':
+        if (flag) dmxConfigPopState();
+        dmxConfigOutput(0, 1, p->comment, "}");
+        break;
+    case '/':
+        dmxConfigOutput(1, 0, NULL, "/");
+        break;
+    default:
+        dmxConfigLog("unknown token %d on line %d\n", p->token, p->line);
+    }
+}
+
+static void dmxConfigPrintToken(DMXConfigTokenPtr p)
+{
+    dmxConfigPrintTokenFlag(p, 1);
+}
+
+static void dmxConfigPrintTokenNopop(DMXConfigTokenPtr p)
+{
+    dmxConfigPrintTokenFlag(p, 0);
+}
+
+static int dmxConfigPrintQuotedString(const char *s)
+{
+    const char *pt;
+
+    if (!s || !s[0]) return 1;  /* Quote empty string */
+    for (pt = s; *pt; ++pt) if (isspace(*pt)) return 1;
+    return 0;
+}
+
+static void dmxConfigPrintString(DMXConfigStringPtr p, int quote)
+{
+    DMXConfigStringPtr pt;
+    
+    if (!p) return;
+    for (pt = p; pt; pt = pt->next) {
+        if (quote && dmxConfigPrintQuotedString(pt->string)) {
+            dmxConfigOutput(1, 0, pt->comment, "\"%s\"",
+                            pt->string ? pt->string : "");
+            } else
+            dmxConfigOutput(1, 0, pt->comment, "%s",
+                            pt->string ? pt->string : "");
+    }
+}
+
+static int dmxConfigPrintPair(DMXConfigPairPtr p, int addSpace)
+{
+    const char *format = NULL;
+    
+    if (!p) return 0;
+    switch (p->token) {
+    case T_ORIGIN:    format = "@%dx%d";   break;
+    case T_DIMENSION: format = "%dx%d";    break;
+    case T_OFFSET:    format = "%c%d%c%d"; break;
+    }
+    if (p->token == T_OFFSET) {
+        if (!p->comment && !p->x && !p->y && p->xsign >= 0 && p->ysign >= 0)
+            return 0;
+        dmxConfigOutput(addSpace, 0, p->comment, format,
+                        p->xsign < 0 ? '-' : '+', p->x,
+                        p->ysign < 0 ? '-' : '+', p->y);
+    } else {
+        if (!p->comment && !p->x && !p->y) return 0;
+        dmxConfigOutput(addSpace, 0, p->comment, format, p->x, p->y);
+    }
+    return 1;
+}
+
+static void dmxConfigPrintDisplay(DMXConfigDisplayPtr p)
+{
+    DMXConfigToken  dummyStart   = { T_DISPLAY, 0, NULL };
+    DMXConfigToken  dummyEnd     = { ';', 0, NULL };
+    DMXConfigToken  dummySep     = { '/', 0, NULL };
+    DMXConfigString dummyName    = { T_STRING, 0, NULL, NULL, NULL };
+    DMXConfigPair   dummySDim    = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
+    DMXConfigPair   dummySOffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
+    DMXConfigPair   dummyRDim    = { T_DIMENSION, 0, NULL, 0, 0, 0, 0 };
+    DMXConfigPair   dummyROffset = { T_OFFSET, 0, NULL, 0, 0, 0, 0 };
+    DMXConfigPair   dummyOrigin  = { T_ORIGIN, 0, NULL, 0, 0, 0, 0 };
+    int             output;
+
+    if (p->dname) p->dname->string = p->name;
+    else          dummyName.string = p->name;
+    
+    if (p->dim && p->dim->scrn && p->dim->scrn->dim) {
+        p->dim->scrn->dim->x    = p->scrnWidth;
+        p->dim->scrn->dim->y    = p->scrnHeight;
+    } else {
+        dummySDim.x             = p->scrnWidth;
+        dummySDim.y             = p->scrnHeight;
+    }
+
+    if (p->dim && p->dim->scrn && p->dim->scrn->offset) {
+        p->dim->scrn->offset->x = p->scrnX;
+        p->dim->scrn->offset->y = p->scrnY;
+    } else {
+        dummySOffset.x          = p->scrnX;
+        dummySOffset.y          = p->scrnY;
+    }
+    
+    if (p->dim && p->dim->root && p->dim->root->dim) {
+        p->dim->root->dim->x    = p->rootWidth;
+        p->dim->root->dim->y    = p->rootHeight;
+    } else {
+        dummyRDim.x             = p->rootWidth;
+        dummyRDim.y             = p->rootHeight;
+    }
+
+    if (p->dim && p->dim->root && p->dim->root->offset) {
+        p->dim->root->offset->x = p->rootX;
+        p->dim->root->offset->y = p->rootY;
+    } else {
+        dummyROffset.x          = p->rootX;
+        dummyROffset.y          = p->rootY;
+    }
+
+    if (p->origin) {
+        p->origin->x     = p->rootXOrigin, p->origin->y     = p->rootYOrigin;
+        p->origin->xsign = p->rootXSign,   p->origin->ysign = p->rootYSign;
+    } else {
+        dummyOrigin.x     = p->rootXOrigin, dummyOrigin.y     = p->rootYOrigin;
+        dummyOrigin.xsign = p->rootXSign,   dummyOrigin.ysign = p->rootYSign;
+    }
+    
+    dmxConfigPrintToken(p->start ? p->start : &dummyStart);
+    dmxConfigPrintString(p->dname ? p->dname : &dummyName, 1);
+
+    if (p->dim && p->dim->scrn && p->dim->scrn->dim)
+        output = dmxConfigPrintPair(p->dim->scrn->dim, 1);
+    else
+        output = dmxConfigPrintPair(&dummySDim, 1);
+    if (p->dim && p->dim->scrn && p->dim->scrn->offset)
+        dmxConfigPrintPair(p->dim->scrn->offset, !output);
+    else
+        dmxConfigPrintPair(&dummySOffset, !output);
+
+    if (p->scrnWidth != p->rootWidth
+        || p->scrnHeight != p->rootHeight
+        || p->rootX
+        || p->rootY) {
+        dmxConfigPrintToken(&dummySep);
+        if (p->dim && p->dim->root && p->dim->root->dim)
+            output = dmxConfigPrintPair(p->dim->root->dim, 1);
+        else
+            output = dmxConfigPrintPair(&dummyRDim, 1);
+        if (p->dim && p->dim->root && p->dim->root->offset)
+            dmxConfigPrintPair(p->dim->root->offset, !output);
+        else
+            dmxConfigPrintPair(&dummyROffset, !output);
+    }
+
+    dmxConfigPrintPair(p->origin ? p->origin : &dummyOrigin, 1);
+    dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
+}
+
+static void dmxConfigPrintWall(DMXConfigWallPtr p)
+{
+    dmxConfigPrintToken(p->start);
+    dmxConfigPrintPair(p->wallDim, 1);
+    dmxConfigPrintPair(p->displayDim, 1);
+    dmxConfigPrintString(p->nameList, 1);
+    dmxConfigPrintToken(p->end);
+}
+
+static void dmxConfigPrintOption(DMXConfigOptionPtr p)
+{
+    DMXConfigToken  dummyStart  = { T_OPTION, 0, NULL };
+    DMXConfigString dummyOption = { T_STRING, 0, NULL, NULL, NULL };
+    DMXConfigToken  dummyEnd    = { ';', 0, NULL };
+
+    dummyOption.string = p->string;
+
+    dmxConfigPrintToken(p->start ? p->start : &dummyStart);
+    dmxConfigPrintString(&dummyOption, 0);
+    dmxConfigPrintToken(p->end ? p->end : &dummyEnd);
+}
+
+static void dmxConfigPrintParam(DMXConfigParamPtr p)
+{
+    if (!p) return;
+    if (p->start) {
+        if (p->open && p->close) {
+            dmxConfigPrintToken(p->start);
+            dmxConfigPrintToken(p->open);
+            dmxConfigPrintParam(p->next);
+            dmxConfigPrintToken(p->close);
+        } else if (p->end && p->param) {
+            dmxConfigPrintToken(p->start);
+            dmxConfigPrintString(p->param, 1);
+            dmxConfigPrintToken(p->end);
+        } else
+            dmxConfigLog("dmxConfigPrintParam: cannot handle format (a)\n");
+    } else if (p->end && p->param) {
+        dmxConfigPrintString(p->param, 1);
+        dmxConfigPrintTokenNopop(p->end);
+        dmxConfigPrintParam(p->next);
+    } else
+        dmxConfigLog("dmxConfigPrintParam: cannot handle format (b)\n");
+}
+
+static void dmxConfigPrintSub(DMXConfigSubPtr p)
+{
+    DMXConfigSubPtr pt;
+
+    if (!p) return;
+    for (pt = p; pt; pt = pt->next) {
+        switch (pt->type) {
+        case dmxConfigComment: dmxConfigPrintComment(pt->comment); break;
+        case dmxConfigDisplay: dmxConfigPrintDisplay(pt->display); break;
+        case dmxConfigWall:    dmxConfigPrintWall(pt->wall);       break;
+        case dmxConfigOption:  dmxConfigPrintOption(pt->option);   break;
+        case dmxConfigParam:   dmxConfigPrintParam(pt->param);     break;
+        default:
+            dmxConfigLog("dmxConfigPrintSub:"
+                         " cannot handle type %d in subentry\n", pt->type);
+        }
+    }
+}
+
+static void dmxConfigPrintVirtual(DMXConfigVirtualPtr p)
+{
+    DMXConfigToken  dummyStart = { T_VIRTUAL, 0, NULL };
+    DMXConfigToken  dummyOpen  = { '{', 0, NULL };
+    DMXConfigToken  dummyClose = { '}', 0, NULL };
+    DMXConfigString dummyName  = { T_STRING, 0, NULL, NULL, NULL };
+    DMXConfigPair   dummyDim   = { T_DIMENSION, 0, NULL, 0, 0 };
+
+    if (p->vname) p->vname->string = p->name;
+    else          dummyName.string = p->name;
+
+    if (p->dim) p->dim->x  = p->width, p->dim->y  = p->height;
+    else        dummyDim.x = p->width, dummyDim.y = p->height;
+    
+
+    dmxConfigPrintToken(p->start ? p->start : &dummyStart);
+    dmxConfigPrintString(p->vname ? p->vname : &dummyName, 1);
+    dmxConfigPrintPair(p->dim ? p->dim : &dummyDim, 1);
+    dmxConfigPrintToken(p->open ? p->open : &dummyOpen);
+    dmxConfigPrintSub(p->subentry);
+    dmxConfigPrintToken(p->close ? p->close : &dummyClose);
+}
+
+/** The configuration information in \a entry will be pretty-printed to
+ * the \a stream.  If \a stream is NULL, then stdout will be used. */
+void dmxConfigPrint(FILE *stream, DMXConfigEntryPtr entry)
+{
+    DMXConfigEntryPtr pt;
+
+    if (!stream) str = stdout;
+    else         str = stream;
+    
+    stack = &initialStack;
+    
+    for (pt = entry; pt; pt = pt->next) {
+        switch (pt->type) {
+        case dmxConfigComment: dmxConfigPrintComment(pt->comment); break;
+        case dmxConfigVirtual: dmxConfigPrintVirtual(pt->virtual); break;
+        default:
+            dmxConfigLog("dmxConfigPrint: cannot handle type %d in entry\n",
+                         pt->type);
+        }
+    }
+    if (pos) dmxConfigNewline();
+}
+
+/** The configuration information in \a p will be pretty-printed to the
+ * \a stream.  If \a stream is NULL, then stdout will be used. */
+void dmxConfigVirtualPrint(FILE *stream, DMXConfigVirtualPtr p)
+{
+    if (!stream) str = stdout;
+    else         str = stream;
+
+    stack = &initialStack;
+    
+    dmxConfigPrintVirtual(p);
+    if (pos) dmxConfigNewline();
+}
diff --git a/hw/dmx/config/dmxprint.h b/hw/dmx/config/dmxprint.h
new file mode 100644
index 000000000..b68c29620
--- /dev/null
+++ b/hw/dmx/config/dmxprint.h
@@ -0,0 +1,44 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+/** \file
+ * Interface to DMX configuration file pretty-printer.  \see dmxprint.c */
+
+#ifndef _DMXPRINT_H_
+#define _DMXPRINT_H_
+
+void dmxConfigPrint(FILE *str, DMXConfigEntryPtr entry);
+void dmxConfigVirtualPrint(FILE *str, DMXConfigVirtualPtr p);
+
+#endif
diff --git a/hw/dmx/config/dmxtodmx.c b/hw/dmx/config/dmxtodmx.c
new file mode 100644
index 000000000..706c49908
--- /dev/null
+++ b/hw/dmx/config/dmxtodmx.c
@@ -0,0 +1,50 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ * This is a simple filter for testing.
+ */
+
+#include "dmxconfig.h"
+#include "dmxparse.h"
+#include "dmxprint.h"
+#include "dmxcompat.h"
+
+extern int                 yyparse(void);
+extern FILE                *yyin;
+
+int main(int argc, char **argv)
+{
+    yydebug = 0;
+    yyparse();
+    dmxConfigPrint(stdout, dmxConfigEntry);
+    return 0;
+}
diff --git a/hw/dmx/config/dmxtodmx.man b/hw/dmx/config/dmxtodmx.man
new file mode 100644
index 000000000..68c7f5b40
--- /dev/null
+++ b/hw/dmx/config/dmxtodmx.man
@@ -0,0 +1,41 @@
+.\" $XFree86$
+.\" Copyright 2002 Red Hat Inc., Durham, North Carolina.
+.\" All Rights Reserved.
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation on the rights to use, copy, modify, merge,
+.\" publish, distribute, sublicense, and/or sell copies of the Software,
+.\" and to permit persons to whom the Software is furnished to do so,
+.\" subject to the following conditions:
+.\"
+.\" The above copyright notice and this permission notice (including the
+.\" next paragraph) shall be included in all copies or substantial
+.\" portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+.\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+.\" BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+.\" ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+.\" CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+.\" SOFTWARE.
+.\"
+.\" Authors:
+.\"   Rickard E. (Rik) Faith <faith@redhat.com>
+.\"
+.TH dmxtodmx 1 __vendorversion__
+.SH NAME
+dmxtodmx - dmx configuration file parser and printer
+.SH SYNOPSIS
+.B dmxtodmx
+.SH DESCRIPTION
+.I dmxtodmx
+reads the standard input, parsing a configuration file for the
+.I Xdmx
+distributed multi-head X server.  After a successful parse, the file is
+pretty-printed to standard output.
+.SH "SEE ALSO"
+Xdmx(1), vdltodmx(1), xdmxconfig(1)
diff --git a/hw/dmx/config/parser.y b/hw/dmx/config/parser.y
new file mode 100644
index 000000000..6adcc8ec9
--- /dev/null
+++ b/hw/dmx/config/parser.y
@@ -0,0 +1,222 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+%{
+#include "dmxparse.h"
+#include <string.h>
+#include <stdlib.h>
+#define YYDEBUG 1
+#define YYERROR_VERBOSE
+#define YY_USE_PROTOS
+
+DMXConfigEntryPtr dmxConfigEntry = NULL;
+#define APPEND(type, h, t)                 \
+{                                          \
+    type pt;                               \
+    for (pt = h; pt->next; pt = pt->next); \
+    pt->next = t;                          \
+}
+%}
+
+%union {
+    DMXConfigTokenPtr      token;
+    DMXConfigStringPtr     string;
+    DMXConfigNumberPtr     number;
+    DMXConfigPairPtr       pair;
+    DMXConfigFullDimPtr    fdim;
+    DMXConfigPartDimPtr    pdim;
+    DMXConfigDisplayPtr    display;
+    DMXConfigWallPtr       wall;
+    DMXConfigOptionPtr     option;
+    DMXConfigParamPtr      param;
+    DMXConfigCommentPtr    comment;
+    DMXConfigSubPtr        subentry;
+    DMXConfigVirtualPtr    virtual;
+    DMXConfigEntryPtr      entry;
+}
+
+				/* Terminals */
+%token <token>   '{' '}' ';' '/' T_VIRTUAL T_DISPLAY T_WALL T_OPTION T_PARAM
+%token <string>  T_STRING
+%token <pair>    T_DIMENSION T_OFFSET T_ORIGIN
+%token <comment> T_COMMENT T_LINE_COMMENT
+
+                                /* Non-termials */
+%type  <token>    Display Wall Terminal Open Close
+%type  <string>   NameList Name
+%type  <pair>     Dimension Offset Origin
+%type  <pdim>     PartialDim
+%type  <fdim>     FullDim
+%type  <display>  DisplayEntry
+%type  <option>   OptionEntry
+%type  <param>    ParamEntry ParamList Param
+%type  <subentry> SubList Sub
+%type  <wall>     WallEntry
+%type  <virtual>  Virtual
+%type  <entry>    Program EntryList Entry
+
+%%
+
+Program : EntryList { dmxConfigEntry = $1; }
+        ;
+
+EntryList : Entry
+          | EntryList Entry { APPEND(DMXConfigEntryPtr,$1,$2); $$ = $1; }
+          ;
+
+Entry : Virtual        { $$ = dmxConfigEntryVirtual($1); }
+      | T_LINE_COMMENT { $$ = dmxConfigEntryComment($1); }
+      ;
+
+Virtual : T_VIRTUAL Open SubList Close
+          { $$ = dmxConfigCreateVirtual($1, NULL, NULL, $2, $3, $4); }
+        | T_VIRTUAL Dimension Open SubList Close
+          { $$ = dmxConfigCreateVirtual($1, NULL, $2, $3, $4, $5); }
+        | T_VIRTUAL Name Open SubList Close
+          { $$ = dmxConfigCreateVirtual($1, $2, NULL, $3, $4, $5); }
+        | T_VIRTUAL Name Dimension Open SubList Close
+          { $$ = dmxConfigCreateVirtual($1, $2, $3, $4, $5, $6 ); }
+        ;
+
+SubList : Sub
+        | SubList Sub { APPEND(DMXConfigSubPtr,$1,$2); $$ = $1; }
+        ;
+
+Sub : T_LINE_COMMENT { $$ = dmxConfigSubComment($1); }
+    | DisplayEntry   { $$ = dmxConfigSubDisplay($1); }
+    | WallEntry      { $$ = dmxConfigSubWall($1); }
+    | OptionEntry    { $$ = dmxConfigSubOption($1); }
+    | ParamEntry     { $$ = dmxConfigSubParam($1); }
+    ;
+
+OptionEntry : T_OPTION NameList Terminal
+              { $$ = dmxConfigCreateOption($1, $2, $3); }
+            ;
+
+ParamEntry : T_PARAM NameList Terminal
+             { $$ = dmxConfigCreateParam($1, NULL, $2, NULL, $3); }
+           | T_PARAM Open ParamList Close
+             { $$ = dmxConfigCreateParam($1, $2, NULL, $4, NULL);
+               $$->next = $3;
+             }
+           ;
+
+ParamList : Param
+          | ParamList Param { APPEND(DMXConfigParamPtr,$1,$2); $$ = $1; }
+          ;
+
+Param : NameList Terminal
+        { $$ = dmxConfigCreateParam(NULL, NULL, $1, NULL, $2); }
+      ;
+
+PartialDim : Dimension Offset
+             { $$ = dmxConfigCreatePartDim($1, $2); }
+           | Dimension
+             { $$ = dmxConfigCreatePartDim($1, NULL); }
+           | Offset
+             { $$ = dmxConfigCreatePartDim(NULL, $1); }
+           ;
+
+FullDim : PartialDim '/' PartialDim
+          { $$ = dmxConfigCreateFullDim($1, $3); }
+        | '/' PartialDim
+          { $$ = dmxConfigCreateFullDim(NULL, $2); }
+        | PartialDim
+          { $$ = dmxConfigCreateFullDim($1, NULL); }
+        ;
+
+DisplayEntry : Display Name FullDim Origin Terminal
+               { $$ = dmxConfigCreateDisplay($1, $2, $3, $4, $5); }
+             | Display FullDim Origin Terminal
+               { $$ = dmxConfigCreateDisplay($1, NULL, $2, $3, $4); }
+             | Display Name Origin Terminal
+               { $$ = dmxConfigCreateDisplay($1, $2, NULL, $3, $4); }
+
+             | Display Name FullDim Terminal
+               { $$ = dmxConfigCreateDisplay($1, $2, $3, NULL, $4); }
+             | Display FullDim Terminal
+               { $$ = dmxConfigCreateDisplay($1, NULL, $2, NULL, $3); }
+             | Display Name Terminal
+               { $$ = dmxConfigCreateDisplay($1, $2, NULL, NULL, $3); }
+             | Display Terminal
+               { $$ = dmxConfigCreateDisplay($1, NULL, NULL, NULL, $2); }
+             ;
+
+WallEntry : Wall Dimension Dimension NameList Terminal
+            { $$ = dmxConfigCreateWall($1, $2, $3, $4, $5); }
+          | Wall Dimension NameList Terminal
+            { $$ = dmxConfigCreateWall($1, $2, NULL, $3, $4); }
+          | Wall NameList Terminal
+            { $$ = dmxConfigCreateWall($1, NULL, NULL, $2, $3); }
+          ;
+
+Display : T_DISPLAY
+        | T_DISPLAY T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+        ;
+
+Name : T_STRING
+     | T_STRING T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+     ;
+
+Dimension : T_DIMENSION
+          | T_DIMENSION T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+          ;
+
+Offset : T_OFFSET
+       | T_OFFSET T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+       ;
+
+Origin : T_ORIGIN
+       | T_ORIGIN T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+       ;
+
+Terminal : ';'
+         | ';' T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+         ;
+
+Open : '{'
+     | '{' T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+     ;
+
+Close : '}'
+      | '}' T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+      ;
+
+Wall : T_WALL
+     | T_WALL T_COMMENT { $$ = $1; $$->comment = $2->comment; }
+     ;
+
+NameList : Name
+         | NameList Name { APPEND(DMXConfigStringPtr, $1, $2); $$ = $1; }
+         ;
diff --git a/hw/dmx/config/scanner.l b/hw/dmx/config/scanner.l
new file mode 100644
index 000000000..fe6d3a15f
--- /dev/null
+++ b/hw/dmx/config/scanner.l
@@ -0,0 +1,179 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+%{
+#include "dmxparse.h"
+#include "parser.h"
+#include <string.h>
+#include <ctype.h>
+static int getdimension(int token, const char *text, int leng);
+static int getstring(int token, const char *text, int leng);
+static int gettoken(int token, const char *text, int leng);
+static int getcomment(int token, const char *text, int leng);
+static int lineno = 1;
+%}
+%s OTHER
+comment         #.*
+word            ([[:alpha:]_/:\-\+\.\*][[:alnum:]_/:\-\+\.\*]+)
+string          \"(([^\"\n])|\"\")*\"
+badstring       \"(([^\"\n])|\"\")*
+number          [[:digit:]x]+
+dimension       [[:digit:]]+[[:blank:]]*x[[:blank:]]*[[:digit:]]+
+offset          [+-][[:digit:]]+[[:blank:]]*[+-][[:blank:]]*[[:digit:]]+
+origin          @[[:blank:]]*[[:digit:]]+[[:blank:]]*[[:blank:]]*x[[:digit:]]+
+NL              \n
+WS              [[:blank:]]+
+%%              
+virtual         return gettoken(T_VIRTUAL, yytext, yyleng);
+display         return gettoken(T_DISPLAY, yytext, yyleng);
+wall            return gettoken(T_WALL, yytext, yyleng);
+option          return gettoken(T_OPTION, yytext, yyleng);
+param           return gettoken(T_PARAM, yytext, yyleng);
+{dimension}     return getdimension(T_DIMENSION, yytext, yyleng);
+{offset}        return getdimension(T_OFFSET, yytext+1, yyleng-1);
+{origin}        return getdimension(T_ORIGIN, yytext+1, yyleng-1);
+{word}          return getstring(T_STRING, yytext, yyleng);
+{string}        return getstring(T_STRING, yytext+1, yyleng-2);
+{NL}            ++lineno;
+{WS}            
+\{              return gettoken(yytext[0], yytext, yyleng);
+\}              return gettoken(yytext[0], yytext, yyleng);
+\;              return gettoken(yytext[0], yytext, yyleng);
+\/              return gettoken(yytext[0], yytext, yyleng);
+^{comment}      return getcomment(T_LINE_COMMENT, yytext, yyleng);
+{comment}       return getcomment(T_COMMENT, yytext, yyleng);
+.               return getstring(T_STRING, yytext, yyleng);
+<<EOF>>         return 0;
+%%
+int yywrap(void)
+{
+    return 1;
+}
+
+void yyerror(const char *message)
+{
+    const char *pt, *end;
+    struct _entry {
+        const char *from;
+        const char *to;
+    } *entry, list[] = {
+        { "T_VIRTUAL",      "\"virtual\"" },
+        { "T_DISPLAY",      "\"display\"" },
+        { "T_WALL",         "\"wall\"" },
+        { "T_OPTION",       "\"option\"" },
+        { "T_PARAM",        "\"param\"" },
+        { "T_DIMENSION",    "dimension (e.g., 2x2 or 1024x768)" },
+        { "T_OFFSET",       "display offset (e.g., +10-10)" },
+        { "T_ORIGIN",       "tile origin (e.g., @1280x1024)" },
+        { "T_STRING",       "string" },
+        { "T_COMMENT",      "comment (e.g., #...)" },
+        { "T_LINE_COMMENT", "comment (e.g., #...)" },
+        { NULL, NULL }
+    };
+    
+    fprintf(stderr, "parse error on line %d at token \"%*.*s\"\n",
+            lineno, yyleng, yyleng, yytext);
+    end = message + strlen(message);
+    for (pt = message; *pt; pt++) {
+        if (pt[0] == 'T' && pt[1] == '_') {
+            const char *next = strchr(pt, ' ');
+            if (!next || !*next) next = strchr(pt, '\0');
+            if (!next) goto bail;
+            --next;
+            if (next-pt == 1 && next[1]
+                && next[2] == '\'' && next[3] == '\'') {
+                fprintf(stderr, "\"%c\"", next[1]);
+                pt += 4;
+                goto cnt;
+            }
+            for (entry = list; entry->from; ++entry) {
+                if (!strncmp(entry->from, pt, strlen(entry->from))) {
+                    fprintf(stderr, "%s", entry->to);
+                    pt = next;
+                    goto cnt;
+                }
+            }
+        } else if (end-pt >= 5 && pt[0] == '\'' && pt[1] == '\'' && pt[3]
+                   && pt[4] == '\'' && pt[5] == '\'') {
+            fprintf(stderr, "\"%c\"", pt[3]);
+            pt += 5;
+        } else if (end-pt >= 3 && pt[0] == '\'' && pt[1] && pt[2] == '\'') {
+            fprintf(stderr, "\"%c\"", pt[1]);
+            pt += 3;
+        }
+      bail:
+        putc(*pt, stderr);
+      cnt:
+        ;
+    }
+    fprintf(stderr, "\n");
+    exit( 1 );
+}
+
+static int getdimension(int token, const char *text, int leng)
+{
+    char *endptr;
+    char *tmp = dmxConfigAlloc(leng+1);
+    int  x, y;
+
+    strncpy(tmp, text, leng);
+    x = strtol(tmp, &endptr, 10);
+    while (*endptr && !isdigit(*endptr)) ++endptr;
+    y = strtol(endptr, NULL, 10);
+    dmxConfigFree(tmp);
+    yylval.pair = dmxConfigCreatePair(token, lineno, NULL, x, y, 1, 1);
+    return token;
+}
+
+static int getstring(int token, const char *text, int leng)
+{
+    yylval.string = dmxConfigCreateString(token, lineno, NULL,
+                                          dmxConfigCopyString(leng ? text : "",
+                                                              leng));
+    return token;
+}
+
+static int gettoken(int token, const char *text, int leng)
+{
+    yylval.token = dmxConfigCreateToken(token, lineno, NULL);
+    return token;
+}
+
+static int getcomment(int token, const char *text, int leng)
+{
+    yylval.comment = dmxConfigCreateComment(token, lineno,
+                                            dmxConfigCopyString(text + 1,
+                                                                leng - 1));
+    return token;
+}
diff --git a/hw/dmx/config/test-a.in b/hw/dmx/config/test-a.in
new file mode 100644
index 000000000..827675374
--- /dev/null
+++ b/hw/dmx/config/test-a.in
@@ -0,0 +1 @@
+error
diff --git a/hw/dmx/config/test-a.out b/hw/dmx/config/test-a.out
new file mode 100644
index 000000000..d35b29fc0
--- /dev/null
+++ b/hw/dmx/config/test-a.out
@@ -0,0 +1,2 @@
+parse error on line 1 at token "error"
+syntax error, unexpected string expecting "virtual" or comment (e.g., #...)
diff --git a/hw/dmx/config/test-b.in b/hw/dmx/config/test-b.in
new file mode 100644
index 000000000..308ec1db4
--- /dev/null
+++ b/hw/dmx/config/test-b.in
@@ -0,0 +1 @@
+# comment
diff --git a/hw/dmx/config/test-b.out b/hw/dmx/config/test-b.out
new file mode 100644
index 000000000..308ec1db4
--- /dev/null
+++ b/hw/dmx/config/test-b.out
@@ -0,0 +1 @@
+# comment
diff --git a/hw/dmx/config/test-c.in b/hw/dmx/config/test-c.in
new file mode 100644
index 000000000..e07ae5ba6
--- /dev/null
+++ b/hw/dmx/config/test-c.in
@@ -0,0 +1 @@
+virtual
diff --git a/hw/dmx/config/test-c.out b/hw/dmx/config/test-c.out
new file mode 100644
index 000000000..7390397a4
--- /dev/null
+++ b/hw/dmx/config/test-c.out
@@ -0,0 +1,2 @@
+parse error on line 2 at token " "
+syntax error, unexpected $end, expecting "{" or string or dimension (e.g., 2x2 or 1024x768)
diff --git a/hw/dmx/config/test-d.in b/hw/dmx/config/test-d.in
new file mode 100644
index 000000000..6827503b1
--- /dev/null
+++ b/hw/dmx/config/test-d.in
@@ -0,0 +1 @@
+display
diff --git a/hw/dmx/config/test-d.out b/hw/dmx/config/test-d.out
new file mode 100644
index 000000000..f3219b301
--- /dev/null
+++ b/hw/dmx/config/test-d.out
@@ -0,0 +1,2 @@
+parse error on line 1 at token "display"
+syntax error, unexpected "display" expecting "virtual" or comment (e.g., #...)
diff --git a/hw/dmx/config/test-e.in b/hw/dmx/config/test-e.in
new file mode 100644
index 000000000..21dbde95c
--- /dev/null
+++ b/hw/dmx/config/test-e.in
@@ -0,0 +1 @@
+display;
diff --git a/hw/dmx/config/test-e.out b/hw/dmx/config/test-e.out
new file mode 100644
index 000000000..f3219b301
--- /dev/null
+++ b/hw/dmx/config/test-e.out
@@ -0,0 +1,2 @@
+parse error on line 1 at token "display"
+syntax error, unexpected "display" expecting "virtual" or comment (e.g., #...)
diff --git a/hw/dmx/config/test-f.in b/hw/dmx/config/test-f.in
new file mode 100644
index 000000000..17f6c6327
--- /dev/null
+++ b/hw/dmx/config/test-f.in
@@ -0,0 +1,2 @@
+virtual {
+}
diff --git a/hw/dmx/config/test-f.out b/hw/dmx/config/test-f.out
new file mode 100644
index 000000000..ba3f441d7
Binary files /dev/null and b/hw/dmx/config/test-f.out differ
diff --git a/hw/dmx/config/test-g.in b/hw/dmx/config/test-g.in
new file mode 100644
index 000000000..453d8121c
--- /dev/null
+++ b/hw/dmx/config/test-g.in
@@ -0,0 +1,4 @@
+virtual a {
+    display d0:0 1280x1024;
+    display d1:0 1280x1024;
+}
diff --git a/hw/dmx/config/test-g.out b/hw/dmx/config/test-g.out
new file mode 100644
index 000000000..453d8121c
--- /dev/null
+++ b/hw/dmx/config/test-g.out
@@ -0,0 +1,4 @@
+virtual a {
+    display d0:0 1280x1024;
+    display d1:0 1280x1024;
+}
diff --git a/hw/dmx/config/test-h.in b/hw/dmx/config/test-h.in
new file mode 100644
index 000000000..1193d309e
--- /dev/null
+++ b/hw/dmx/config/test-h.in
@@ -0,0 +1,7 @@
+# comment a
+# comment b
+## comment c
+#	<-- tab
+# Next comment is empty
+#
+# Non empty
diff --git a/hw/dmx/config/test-h.out b/hw/dmx/config/test-h.out
new file mode 100644
index 000000000..1193d309e
--- /dev/null
+++ b/hw/dmx/config/test-h.out
@@ -0,0 +1,7 @@
+# comment a
+# comment b
+## comment c
+#	<-- tab
+# Next comment is empty
+#
+# Non empty
diff --git a/hw/dmx/config/test-i.in b/hw/dmx/config/test-i.in
new file mode 100644
index 000000000..b65967e62
--- /dev/null
+++ b/hw/dmx/config/test-i.in
@@ -0,0 +1,3 @@
+virtual a {
+    param a b; # comment
+}
diff --git a/hw/dmx/config/test-i.out b/hw/dmx/config/test-i.out
new file mode 100644
index 000000000..b65967e62
--- /dev/null
+++ b/hw/dmx/config/test-i.out
@@ -0,0 +1,3 @@
+virtual a {
+    param a b; # comment
+}
diff --git a/hw/dmx/config/test-j.in b/hw/dmx/config/test-j.in
new file mode 100644
index 000000000..cc1cdd0ea
--- /dev/null
+++ b/hw/dmx/config/test-j.in
@@ -0,0 +1,13 @@
+virtual a {
+    option aaa # bbb
+           aa cc;
+    param { # comment 1
+        a b;
+        c d;
+        x y z; # comment 2
+    }
+    param e f g h; # comment 3
+    param e f g hlskdjflskdfjsd #comment 4
+     flksdjf sldkfjsldkfjsdlf
+    "lsdkfjsldkfjlsdkjflskdjflsdkjfl" "lkjsdlfjsdlfkjsdlfj";
+}
diff --git a/hw/dmx/config/test-j.out b/hw/dmx/config/test-j.out
new file mode 100644
index 000000000..9acaa009b
--- /dev/null
+++ b/hw/dmx/config/test-j.out
@@ -0,0 +1,11 @@
+virtual a {
+    option "aaa aa cc";
+    param { # comment 1
+            a b;
+            c d;
+            x y z; # comment 2
+    }
+    param e f g h; # comment 3
+    param e f g hlskdjflskdfjsd #comment 4
+        flksdjf sldkfjsldkfjsdlf lsdkfjsldkfjlsdkjflskdjflsdkjfl lkjsdlfjsdlfkjsdlfj;
+}
diff --git a/hw/dmx/config/test-k.in b/hw/dmx/config/test-k.in
new file mode 100644
index 000000000..2218d26d9
--- /dev/null
+++ b/hw/dmx/config/test-k.in
@@ -0,0 +1,3 @@
+virtual a {
+    option +xinerama -syncbatch 0;
+}
diff --git a/hw/dmx/config/test-k.out b/hw/dmx/config/test-k.out
new file mode 100644
index 000000000..ebd743923
--- /dev/null
+++ b/hw/dmx/config/test-k.out
@@ -0,0 +1,3 @@
+virtual a {
+    option "+xinerama -syncbatch 0";
+}
diff --git a/hw/dmx/config/test-l.in b/hw/dmx/config/test-l.in
new file mode 100644
index 000000000..7bf05139b
--- /dev/null
+++ b/hw/dmx/config/test-l.in
@@ -0,0 +1,12 @@
+virtual a {
+    display d0:0 1x2;
+    display d1:0 +3+4;
+    display d2:0 100x200 @1x1;
+    display d3:0 +3+4 @2x2;
+    display d4:0 100x200+3+4 @3x3;
+    display d5:0 / 1x2+3+4;
+    display d6:0 / 1x2;
+    display d7:0 / +3+4;
+    display d8:0 / 1x2+3+4 @4x4;
+    display d9:0 11x22+33+44 / 111x222+333+444 @1000x1100;
+}
diff --git a/hw/dmx/config/test-l.out b/hw/dmx/config/test-l.out
new file mode 100644
index 000000000..7bf05139b
--- /dev/null
+++ b/hw/dmx/config/test-l.out
@@ -0,0 +1,12 @@
+virtual a {
+    display d0:0 1x2;
+    display d1:0 +3+4;
+    display d2:0 100x200 @1x1;
+    display d3:0 +3+4 @2x2;
+    display d4:0 100x200+3+4 @3x3;
+    display d5:0 / 1x2+3+4;
+    display d6:0 / 1x2;
+    display d7:0 / +3+4;
+    display d8:0 / 1x2+3+4 @4x4;
+    display d9:0 11x22+33+44 / 111x222+333+444 @1000x1100;
+}
diff --git a/hw/dmx/config/vdltodmx.c b/hw/dmx/config/vdltodmx.c
new file mode 100644
index 000000000..33cd8fe8a
--- /dev/null
+++ b/hw/dmx/config/vdltodmx.c
@@ -0,0 +1,58 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+#include "dmxconfig.h"
+#include "dmxparse.h"
+#include "dmxprint.h"
+#include "dmxcompat.h"
+
+int main(int argc, char **argv)
+{
+    DMXConfigEntryPtr entry;
+    FILE              *str;
+
+    if (argc != 2 && argc !=3) {
+        fprintf(stderr, "Usage: vdltodmx inFile [outFile]\n");
+        return 1;
+    }
+    if (argc == 2) {
+        str = stdout;
+    } else if (!(str = fopen(argv[2], "w"))) {
+        fprintf(stderr, "Cannot open %s for write\n", argv[2]);
+        return 2;
+    }
+    entry = dmxVDLRead(argv[1]);
+    dmxConfigPrint(str, entry);
+    return 0;
+}
diff --git a/hw/dmx/config/vdltodmx.man b/hw/dmx/config/vdltodmx.man
new file mode 100644
index 000000000..b733db7ab
--- /dev/null
+++ b/hw/dmx/config/vdltodmx.man
@@ -0,0 +1,95 @@
+.\" $XFree86$
+.\" Copyright 2002 Red Hat Inc., Durham, North Carolina.
+.\" All Rights Reserved.
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation on the rights to use, copy, modify, merge,
+.\" publish, distribute, sublicense, and/or sell copies of the Software,
+.\" and to permit persons to whom the Software is furnished to do so,
+.\" subject to the following conditions:
+.\"
+.\" The above copyright notice and this permission notice (including the
+.\" next paragraph) shall be included in all copies or substantial
+.\" portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+.\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+.\" BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+.\" ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+.\" CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+.\" SOFTWARE.
+.\"
+.\" Authors:
+.\"   Rickard E. (Rik) Faith <faith@redhat.com>
+.\"
+.TH vdltodmx 1 __vendorversion__
+.SH NAME
+vdltodmx - dmx configuration file parser and printer
+.SH SYNOPSIS
+.B vdltodmx
+.I infile
+.I outfile
+.SH DESCRIPTION
+.I vdltodmx
+reads the input file, which should be in VDL configuration file format.
+After a successful parse, a file in Xdmx configuration file format is
+written to the output file.
+.P
+The VDL file format is used with
+.IR xmovie ,
+which is available from
+http://www.llnl.gov/icc/lc/img/xmovie/xmovie.html
+.SH EXAMPLE
+Given the following VDL-format file:
+.RS
+.nf
+0
+2
+#
+#
+2560 2048 Left two-thirds [restrict=*:2]
+2
+:2.1 1280 2048   0    0 0 0
+:2.2 1280 2048   1280 0 0 0
+4
+1280 1024 0      0
+1280 1024 0      1024
+1280 1024 1280   0
+1280 1024 1280   1024
+#
+2560 2048 Right two-thirds [restrict=*:2]
+2
+:2.2 1280 2048   0   0 0 0
+:2.3 1280 2048   1280 0 0 0
+4
+1280 1024 1280    0
+1280 1024 1280    1024
+1280 1024 2560 0
+1280 1024 2560 1024
+.fi
+.RE
+the following DMX-format file will be produced:
+.RS
+.nf
+#
+#
+virtual "Left two-thirds" 2560x2048 {
+    display :2.1 1280x2048;
+    display :2.2 1280x2048 @1280x0;
+}
+#
+virtual "Right two-thirds" 2560x2048 {
+    display :2.2 1280x2048;
+    display :2.3 1280x2048 @1280x0;
+}
+.fi
+.RE
+.SH BUGS
+If the VDL file is not in the expected format, the program will probably
+dump core.
+.SH "SEE ALSO"
+Xdmx(1), xdmxconfig(1), vdl(3), xmovie(1)
diff --git a/hw/dmx/config/xdmxconfig.c b/hw/dmx/config/xdmxconfig.c
new file mode 100644
index 000000000..1ba59b67e
--- /dev/null
+++ b/hw/dmx/config/xdmxconfig.c
@@ -0,0 +1,1188 @@
+/* $XFree86$ */
+/*
+ * Copyright 2002 Red Hat Inc., Durham, North Carolina.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@redhat.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Box.h>
+/* #include <X11/Xaw/Paned.h> */
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/keysym.h>
+#include <X11/Xmu/SysUtil.h>
+#include "Canvas.h"
+
+#include "dmxparse.h"
+#include "dmxprint.h"
+#include "dmxlog.h"
+
+extern int                 yyparse(void);
+extern FILE                *yyin;
+
+#define DMX_INFO "xdmxconfig v0.9\nCopyright 2002 Red Hat Inc.\n$Id$"
+
+#define DMX_MAIN_WIDTH    800
+#define DMX_MAIN_HEIGHT   600
+#define DMX_DATA_WIDTH    200
+#define DMX_DATA_HEIGHT   200
+#define DMX_CANVAS_WIDTH  400
+#define DMX_CANVAS_HEIGHT 500
+
+DMXConfigEntryPtr          dmxConfigEntry;
+static DMXConfigVirtualPtr dmxConfigCurrent, dmxConfigNewVirtual;
+static DMXConfigDisplayPtr dmxConfigCurrentDisplay, dmxConfigNewDisplay;
+static int                 dmxConfigGrabbed, dmxConfigGrabbedFine;
+static int                 dmxConfigGrabbedX, dmxConfigGrabbedY;
+static char                *dmxConfigFilename;
+static GC                  dmxConfigGC, dmxConfigGCRev, dmxConfigGCHL;
+static int                 dmxConfigGCInit = 0;
+static Dimension           dmxConfigWidgetWidth, dmxConfigWidgetHeight;
+static Dimension           dmxConfigWallWidth, dmxConfigWallHeight;
+static double              dmxConfigScaleX, dmxConfigScaleY;
+static int                 dmxConfigNotSaved;
+static enum {
+    dmxConfigStateOpen,
+    dmxConfigStateSave
+}                          dmxConfigState;
+
+/* Global widgets */
+static Widget              canvas;
+static Widget              cnamebox, cdimbox;
+static Widget              openpopup, opendialog;
+static Widget              namebox, dimbox, rtbox, origbox;
+static Widget              okbutton, buttonpopup;
+static Widget              ecbutton, dcbutton;
+static Widget              ndbutton0, ndbutton1, edbutton, ddbutton;
+static Widget              ecpopup, ecdialog0, ecdialog1;
+static Widget              edpopup, eddialog0, eddialog1, eddialog2;
+static Widget              aboutpopup, quitpopup;
+
+static void dmxConfigCanvasGCs(void)
+{
+    Display       *dpy = XtDisplay(canvas);
+    Window        win  = XtWindow(canvas);
+    XGCValues     gcvals;
+    unsigned long mask;
+    Colormap      colormap;
+    XColor        fg, bg, hl, tmp;
+    
+    if (dmxConfigGCInit++) return;
+
+    XtVaGetValues(canvas, XtNcolormap, &colormap, NULL);
+    XAllocNamedColor(XtDisplay(canvas), colormap, "black", &bg, &tmp);
+    XAllocNamedColor(XtDisplay(canvas), colormap, "white", &fg, &tmp);
+    XAllocNamedColor(XtDisplay(canvas), colormap, "red",   &hl, &tmp);
+
+    mask = (GCFunction | GCPlaneMask | GCClipMask | GCForeground |
+            GCBackground | GCLineWidth | GCLineStyle | GCCapStyle |
+            GCFillStyle);
+
+                                /* FIXME: copy this from widget */
+    gcvals.function   = GXcopy;
+    gcvals.plane_mask = AllPlanes;
+    gcvals.clip_mask  = None;
+    gcvals.foreground = fg.pixel;
+    gcvals.background = bg.pixel;
+    gcvals.line_width = 0;
+    gcvals.line_style = LineSolid;
+    gcvals.cap_style  = CapNotLast;
+    gcvals.fill_style = FillSolid;
+    
+    dmxConfigGC       = XCreateGC(dpy, win, mask, &gcvals);
+    gcvals.foreground = hl.pixel;
+    dmxConfigGCHL     = XCreateGC(dpy, win, mask, &gcvals);
+    gcvals.foreground = bg.pixel;
+    gcvals.background = fg.pixel;
+    dmxConfigGCRev    = XCreateGC(dpy, win, mask, &gcvals);
+}
+
+static void dmxConfigGetDims(int *maxWidth, int *maxHeight)
+{
+    DMXConfigSubPtr   pt;
+    DMXConfigEntryPtr e;
+    
+    *maxWidth = dmxConfigWallWidth  = 0;
+    *maxWidth = dmxConfigWallHeight = 0;
+    if (!dmxConfigCurrent) return;
+    
+    dmxConfigWallWidth  = dmxConfigCurrent->width;
+    dmxConfigWallHeight = dmxConfigCurrent->height;
+    if (!dmxConfigWallWidth || !dmxConfigWallHeight) {
+        for (pt = dmxConfigCurrent->subentry; pt; pt = pt->next) {
+            if (pt->type == dmxConfigDisplay) {
+                int x = pt->display->scrnWidth  + pt->display->rootXOrigin;
+                int y = pt->display->scrnHeight + pt->display->rootYOrigin;
+                if (x > dmxConfigWallWidth)  dmxConfigWallWidth  = x;
+                if (y > dmxConfigWallHeight) dmxConfigWallHeight = y;
+            }
+        }
+    }
+                                /* Compute maximums */
+    *maxWidth = *maxHeight = 0;
+    for (e = dmxConfigEntry; e; e = e->next) {
+        if (e->type != dmxConfigVirtual) continue;
+        for (pt = e->virtual->subentry; pt; pt = pt->next) {
+            if (pt->type == dmxConfigDisplay) {
+                int x = pt->display->scrnWidth  + pt->display->rootXOrigin;
+                int y = pt->display->scrnHeight + pt->display->rootYOrigin;
+                if (x > *maxWidth)  *maxWidth  = x;
+                if (y > *maxHeight) *maxHeight = y;
+            }
+        }
+    }
+    if (dmxConfigWallWidth  > *maxWidth)  *maxWidth  = dmxConfigWallWidth;
+    if (dmxConfigWallHeight > *maxHeight) *maxHeight = dmxConfigWallHeight;
+}
+
+static int scalex(int x)   { return (int)((x * dmxConfigScaleX) + .5); }
+static int scaley(int y)   { return (int)((y * dmxConfigScaleY) + .5); }
+static int unscalex(int x) { return (int)((x / dmxConfigScaleX) + .5); }
+static int unscaley(int y) { return (int)((y / dmxConfigScaleY) + .5); }
+
+static void dmxConfigDataUpdate(void)
+{
+                                /* FIXME: could result in buffer overflows */
+    char       cnambuf[512];
+    char       cdimbuf[128];
+    char       nambuf[512];
+    char       dimbuf[128];
+    char       rtbuf[128];
+    char       offbuf[128];
+    const char *name;
+
+    if (!dmxConfigCurrent) {
+        XtVaSetValues(cnamebox,   XtNlabel, "", XtNsensitive, False, NULL);
+        XtVaSetValues(cdimbox,    XtNlabel, "", XtNsensitive, False, NULL);
+        XtVaSetValues(ecbutton,                 XtNsensitive, False, NULL);
+        XtVaSetValues(dcbutton,                 XtNsensitive, False, NULL);
+        XtVaSetValues(ndbutton0,                XtNsensitive, False, NULL);
+        XtVaSetValues(ndbutton1,                XtNsensitive, False, NULL);
+    } else {
+        name = dmxConfigCurrent->name;
+        XmuSnprintf(cnambuf, sizeof(cnambuf), "%s", name ? name : "");
+	XmuSnprintf(cdimbuf, sizeof(cdimbuf), "%dx%d",
+                    dmxConfigWallWidth, dmxConfigWallHeight);
+        XtVaSetValues(cnamebox,   XtNlabel, cnambuf, XtNsensitive, True, NULL);
+        XtVaSetValues(cdimbox,    XtNlabel, cdimbuf, XtNsensitive, True, NULL);
+        XtVaSetValues(ecbutton,                      XtNsensitive, True, NULL);
+        XtVaSetValues(dcbutton,                      XtNsensitive, True, NULL);
+        XtVaSetValues(ndbutton0,                     XtNsensitive, True, NULL);
+        XtVaSetValues(ndbutton1,                     XtNsensitive, True, NULL);
+    }
+    
+    if (!dmxConfigCurrentDisplay) {
+        XtVaSetValues(namebox, XtNlabel, "", XtNsensitive, False, NULL);
+        XtVaSetValues(dimbox,  XtNlabel, "", XtNsensitive, False, NULL);
+        XtVaSetValues(rtbox,   XtNlabel, "", XtNsensitive, False, NULL);
+        XtVaSetValues(origbox, XtNlabel, "", XtNsensitive, False, NULL);
+        XtVaSetValues(edbutton,              XtNsensitive, False, NULL);
+        XtVaSetValues(ddbutton,              XtNsensitive, False, NULL);
+    } else {
+        name = dmxConfigCurrentDisplay->name;
+        XmuSnprintf(nambuf, sizeof(nambuf), "%s", name ? name : "");
+        XmuSnprintf(dimbuf, sizeof(dimbuf), "%dx%d%c%d%c%d",
+                    dmxConfigCurrentDisplay->scrnWidth,
+                    dmxConfigCurrentDisplay->scrnHeight,
+                    dmxConfigCurrentDisplay->scrnXSign < 0 ? '-' : '+',
+                    dmxConfigCurrentDisplay->scrnX,
+                    dmxConfigCurrentDisplay->scrnYSign < 0 ? '-' : '+',
+                    dmxConfigCurrentDisplay->scrnY);
+        XmuSnprintf(rtbuf, sizeof(dimbuf), "%dx%d%c%d%c%d",
+                    dmxConfigCurrentDisplay->rootWidth,
+                    dmxConfigCurrentDisplay->rootHeight,
+                    dmxConfigCurrentDisplay->rootXSign < 0 ? '-' : '+',
+                    dmxConfigCurrentDisplay->rootX,
+                    dmxConfigCurrentDisplay->rootYSign < 0 ? '-' : '+',
+                    dmxConfigCurrentDisplay->rootY);
+        XmuSnprintf(offbuf, sizeof(offbuf), "@%dx%d",
+                    dmxConfigCurrentDisplay->rootXOrigin,
+                    dmxConfigCurrentDisplay->rootYOrigin);
+        XtVaSetValues(namebox, XtNlabel, nambuf, XtNsensitive, True, NULL);
+        XtVaSetValues(dimbox,  XtNlabel, dimbuf, XtNsensitive, True, NULL);
+        XtVaSetValues(rtbox,   XtNlabel, rtbuf,  XtNsensitive, True, NULL);
+        XtVaSetValues(origbox, XtNlabel, offbuf, XtNsensitive, True, NULL);
+        XtVaSetValues(edbutton,                  XtNsensitive, True, NULL);
+        XtVaSetValues(ddbutton,                  XtNsensitive, True, NULL);
+    }
+}
+
+static void dmxConfigCanvasUpdate(void)
+{
+    DMXConfigSubPtr pt;
+    Display         *dpy     = XtDisplay(canvas);
+    Window          win      = XtWindow(canvas);
+    GContext        gcontext = XGContextFromGC(dmxConfigGC);
+    XFontStruct     *fs;
+    int             w, h;
+
+    XFillRectangle(dpy, win, dmxConfigGCRev,
+                   0, 0, dmxConfigWidgetWidth, dmxConfigWidgetHeight);
+    dmxConfigDataUpdate();
+    if (!dmxConfigCurrent) return;
+
+    w = scalex(dmxConfigWallWidth);
+    h = scaley(dmxConfigWallHeight);
+    if (w > dmxConfigWidgetWidth - 1)  w = dmxConfigWidgetWidth - 1;
+    if (h > dmxConfigWidgetHeight - 1) h = dmxConfigWidgetHeight - 1;
+    XDrawRectangle(dpy, win, dmxConfigGC, 0, 0, w, h);
+    fs = XQueryFont(dpy, gcontext);
+    for (pt = dmxConfigCurrent->subentry; pt; pt = pt->next) {
+        int x, y, len;
+        int xo = 3, yo = fs->ascent + fs->descent + 2;
+        GC  gc;
+
+        if (pt->type != dmxConfigDisplay) continue;
+        gc  = (pt->display == dmxConfigCurrentDisplay
+               ? dmxConfigGCHL
+               : dmxConfigGC);
+        x   = scalex(pt->display->rootXOrigin);
+        y   = scaley(pt->display->rootYOrigin);
+        w   = scalex(pt->display->scrnWidth);
+        h   = scaley(pt->display->scrnHeight);
+        len = pt->display->name ? strlen(pt->display->name) : 0;
+        if (x > dmxConfigWidgetWidth - 1)  x = dmxConfigWidgetWidth - 1;
+        if (y > dmxConfigWidgetHeight - 1) y = dmxConfigWidgetHeight - 1;
+        XDrawRectangle(dpy, win, gc, x, y, w, h);
+        if (fs && len) {
+            while (len && XTextWidth(fs, pt->display->name, len) >= w - 2 * xo)
+                --len;
+            if (len)
+                XDrawString(dpy, win, gc, x+xo, y+yo, pt->display->name, len);
+        }
+    }
+    if (fs) XFreeFontInfo(NULL, fs, 0);
+}
+
+static void dmxConfigCanvasDraw(Region region)
+{
+    Display *dpy = XtDisplay(canvas);
+    int     maxWidth, maxHeight;
+    
+    dmxConfigCanvasGCs();
+    if (region) {
+        XSetRegion(dpy, dmxConfigGC,    region);
+        XSetRegion(dpy, dmxConfigGCRev, region);
+        XSetRegion(dpy, dmxConfigGCHL,  region);
+    }
+    XtVaGetValues(canvas,
+                  XtNwidth, &dmxConfigWidgetWidth,
+                  XtNheight, &dmxConfigWidgetHeight,
+                  NULL);
+    dmxConfigGetDims(&maxWidth, &maxHeight);
+    dmxConfigScaleX = (double)dmxConfigWidgetWidth  / maxWidth;
+    dmxConfigScaleY = (double)dmxConfigWidgetHeight / maxHeight;
+    if (dmxConfigScaleX > dmxConfigScaleY) dmxConfigScaleX = dmxConfigScaleY;
+    if (dmxConfigScaleY > dmxConfigScaleX) dmxConfigScaleY = dmxConfigScaleX;
+    dmxConfigCanvasUpdate();
+    if (region) {
+        XSetClipMask(dpy, dmxConfigGC,    None);
+        XSetClipMask(dpy, dmxConfigGCRev, None);
+        XSetClipMask(dpy, dmxConfigGCHL,  None);
+    }
+}
+
+static void dmxConfigSelectCallback(Widget w, XtPointer closure,
+                                    XtPointer callData)
+{
+    dmxConfigCurrent = closure;
+    dmxConfigVirtualPrint(stdout, dmxConfigCurrent);
+    dmxConfigCanvasDraw(NULL);
+}
+
+static void dmxConfigCopystrings(void)
+{
+    DMXConfigEntryPtr pt;
+    DMXConfigSubPtr   sub;
+
+    if (!dmxConfigCurrent) return;
+
+                                /* FIXME: this is all a per-config file
+                                 * memory leak */
+    for (pt = dmxConfigEntry; pt; pt = pt->next) {
+        if (pt->type == dmxConfigVirtual) {
+            pt->virtual->name = XtNewString(pt->virtual->name
+                                            ? pt->virtual->name
+                                            : "");
+
+            for (sub = pt->virtual->subentry; sub; sub = sub->next) {
+                if (sub->type != dmxConfigDisplay) continue;
+                sub->display->name = XtNewString(sub->display->name
+                                                 ? sub->display->name
+                                                 : "");
+            }
+        }
+    }
+}
+
+static void dmxConfigGetValueString(char **d, Widget w)
+{
+    const char *tmp = XawDialogGetValueString(w);
+    if (*d) XtFree(*d);
+    *d = XtNewString(tmp);
+}
+
+static void dmxConfigSetupCnamemenu(void)
+{
+    static Widget     cnamemenu = NULL;
+    Widget            w;
+    DMXConfigEntryPtr pt;
+
+    if (cnamemenu) XtDestroyWidget(cnamemenu);
+    cnamemenu = NULL;
+
+    if (!dmxConfigCurrent) return;
+    cnamemenu = XtVaCreatePopupShell("cnamemenu", simpleMenuWidgetClass,
+                                     cnamebox,
+                                     NULL);
+    
+    for (pt = dmxConfigEntry; pt; pt = pt->next) {
+        if (pt->type == dmxConfigVirtual) {
+            w = XtVaCreateManagedWidget(pt->virtual->name
+                                        ? pt->virtual->name
+                                        : "",
+                                        smeBSBObjectClass, cnamemenu,
+                                        NULL);
+            XtAddCallback(w, XtNcallback,
+                          dmxConfigSelectCallback, pt->virtual);
+        }
+    }
+}
+
+static void dmxConfigReadFile(void)
+{
+    FILE              *str;
+    DMXConfigEntryPtr pt;
+
+    if (!(str = fopen(dmxConfigFilename, "r"))) {
+        dmxLog(dmxWarning, "Unable to read configuration file %s\n",
+               dmxConfigFilename);
+        return;
+    }
+    yyin    = str;
+    yydebug = 0;
+    yyparse();
+    fclose(str);
+    dmxLog(dmxInfo, "Read configuration file %s\n", dmxConfigFilename);
+
+    for (pt = dmxConfigEntry; pt; pt = pt->next) {
+        if (pt->type == dmxConfigVirtual) {
+            dmxConfigCurrent = pt->virtual;
+            break;
+        }
+    }
+
+    
+    
+    if (XtIsRealized(canvas)) {
+        dmxConfigCopystrings();
+        dmxConfigSetupCnamemenu();
+        dmxConfigCanvasDraw(NULL);
+    }
+    dmxConfigVirtualPrint(stdout, dmxConfigCurrent);
+}
+
+static void dmxConfigWriteFile(void)
+{
+    FILE              *str;
+
+    if (!(str = fopen(dmxConfigFilename, "w"))) {
+        dmxLog(dmxWarning, "Unable to write configuration file %s\n",
+               dmxConfigFilename);
+        return;
+    }
+    dmxConfigPrint(str, dmxConfigEntry);
+    fclose(str);
+}
+
+static DMXConfigDisplayPtr dmxConfigFindDisplay(int x, int y)
+{
+    DMXConfigSubPtr pt;
+
+    if (!dmxConfigCurrent) return NULL;
+    for (pt = dmxConfigCurrent->subentry; pt; pt = pt->next) {
+        DMXConfigDisplayPtr d = pt->display;
+        if (pt->type != dmxConfigDisplay) continue;
+        if (x >= scalex(d->rootXOrigin)
+            && x <= scalex(d->rootXOrigin + d->scrnWidth)
+            && y >= scaley(d->rootYOrigin)
+            && y <= scaley(d->rootYOrigin + d->scrnHeight)) return d;
+    }
+    return NULL;
+}
+
+static void dmxConfigSetPopupPosition(Widget popup)
+{
+    Position     x, y;
+    Window       t1, t2;
+    int          root_x, root_y;
+    int          temp_x, temp_y;
+    unsigned int temp;
+
+
+    XtRealizeWidget(popup);
+    if (!XQueryPointer(XtDisplay(popup), XtWindow(popup), &t1, &t2,
+                       &root_x, &root_y, &temp_x, &temp_y, &temp))
+        root_x = root_y = 0;
+
+    x = root_x - 5;
+    y = root_y - 5;
+    XtVaSetValues(popup, XtNx, x, XtNy, y, NULL);
+}
+
+static void dmxConfigPlaceMenu(Widget w, XEvent *event,
+                               String *params, Cardinal *num_params)
+{
+    dmxConfigSetPopupPosition(buttonpopup);
+}
+
+static void dmxConfigMove(int deltaX, int deltaY)
+{
+    dmxConfigCurrentDisplay->rootXOrigin += deltaX;
+    dmxConfigCurrentDisplay->rootYOrigin += deltaY;
+    if (dmxConfigCurrentDisplay->rootXOrigin < 0)
+        dmxConfigCurrentDisplay->rootXOrigin = 0;
+    if (dmxConfigCurrentDisplay->rootYOrigin < 0)
+        dmxConfigCurrentDisplay->rootYOrigin = 0;
+    if (dmxConfigWallWidth && dmxConfigWallHeight) {
+        if (dmxConfigCurrentDisplay->rootXOrigin >= dmxConfigWallWidth)
+            dmxConfigCurrentDisplay->rootXOrigin = dmxConfigWallWidth - 1;
+        if (dmxConfigCurrentDisplay->rootYOrigin >= dmxConfigWallHeight)
+            dmxConfigCurrentDisplay->rootYOrigin = dmxConfigWallHeight - 1;
+    }
+    dmxConfigCanvasUpdate();
+    dmxConfigNotSaved = 1;
+}
+
+static void dmxConfigCanvasInput(Widget w, XtPointer closure,
+                                 XtPointer callData)
+{
+    XEvent              *e      = (XEvent *)callData;
+    DMXConfigDisplayPtr display = NULL;
+
+    switch (e->type) {
+    case ButtonPress:
+        if (e->xbutton.button == Button1) {
+            dmxConfigGrabbed     = 1;
+            dmxConfigGrabbedFine = 0;
+            dmxConfigGrabbedX    = e->xbutton.x;
+            dmxConfigGrabbedY    = e->xbutton.y;
+        }
+        if (e->xbutton.button == Button2) {
+            dmxConfigGrabbed     = 1;
+            dmxConfigGrabbedFine = 1;
+            dmxConfigGrabbedX    = e->xbutton.x;
+            dmxConfigGrabbedY    = e->xbutton.y;
+        }
+        break;
+    case ButtonRelease:
+        if (e->xbutton.button == Button1) dmxConfigGrabbed = 0;
+        if (e->xbutton.button == Button2) dmxConfigGrabbed = 0;
+        break;
+    case MotionNotify:
+        if (dmxConfigGrabbed && dmxConfigCurrentDisplay) {
+            int deltaX = e->xmotion.x - dmxConfigGrabbedX;
+            int deltaY = e->xmotion.y - dmxConfigGrabbedY;
+            dmxConfigMove(dmxConfigGrabbedFine ? deltaX : unscalex(deltaX),
+                          dmxConfigGrabbedFine ? deltaY : unscaley(deltaY));
+            dmxConfigGrabbedX = e->xmotion.x;
+            dmxConfigGrabbedY = e->xmotion.y;
+        } else {
+            display = dmxConfigFindDisplay(e->xmotion.x, e->xmotion.y);
+            if (display != dmxConfigCurrentDisplay) {
+                dmxConfigCurrentDisplay = display;
+                dmxConfigCanvasUpdate();
+            }
+        }
+        break;
+    case KeyPress:
+        switch (XLookupKeysym(&e->xkey, 0)) {
+        case XK_Right: dmxConfigMove(1,0);  break;
+        case XK_Left:  dmxConfigMove(-1,0); break;
+        case XK_Down:  dmxConfigMove(0,1);  break;
+        case XK_Up:    dmxConfigMove(0,-1); break;
+        }
+        break;
+    }
+}
+
+static void dmxConfigCanvasResize(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    dmxConfigCanvasDraw(NULL);
+}
+
+static void dmxConfigCanvasExpose(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    CanvasExposeDataPtr data = (CanvasExposeDataPtr)callData;
+
+    dmxConfigCanvasDraw(data->region);
+}
+
+static void dmxConfigOpenCallback(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    dmxConfigState = dmxConfigStateOpen;
+    XtVaSetValues(okbutton, XtNlabel, "Open", NULL);
+    dmxConfigSetPopupPosition(openpopup);
+    XtPopup(openpopup, XtGrabExclusive);
+}
+
+static void dmxConfigSaveCallback(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    dmxConfigState = dmxConfigStateSave;
+    XtVaSetValues(okbutton, XtNlabel, "Save", NULL);
+    dmxConfigSetPopupPosition(openpopup);
+    XtPopup(openpopup, XtGrabExclusive);
+}
+
+static void dmxConfigOkCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    dmxConfigGetValueString(&dmxConfigFilename, opendialog);
+    XtPopdown(openpopup);
+    if (dmxConfigState == dmxConfigStateOpen) dmxConfigReadFile();
+    else                                      dmxConfigWriteFile();
+    dmxConfigNotSaved = 0;
+}
+
+static void dmxConfigCanCallback(Widget w, XtPointer closure,
+                                 XtPointer callData)
+{
+    XtPopdown(openpopup);
+}
+
+static void dmxConfigECCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    char buf[256];              /* RATS: Only used in XmuSnprintf */
+    
+    if (!dmxConfigCurrent) return;
+    dmxConfigSetPopupPosition(ecpopup);
+    XtVaSetValues(ecdialog0, XtNvalue,
+                  dmxConfigCurrent->name ? dmxConfigCurrent->name : "",
+                  NULL);
+    XmuSnprintf(buf, sizeof(buf), "%dx%d",
+                dmxConfigCurrent->width, dmxConfigCurrent->height);
+    XtVaSetValues(ecdialog1, XtNvalue, buf, NULL);
+    XtPopup(ecpopup, XtGrabExclusive);
+}
+
+static void dmxConfigNCCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    int width = 1280*2, height = 1024*2;
+
+    if (dmxConfigCurrent) {
+        width  = dmxConfigCurrent->width;
+        height = dmxConfigCurrent->height;
+    }
+
+    dmxConfigCurrent         = dmxConfigCreateVirtual(NULL, NULL, NULL,
+                                                      NULL, NULL, NULL);
+    dmxConfigNewVirtual      = dmxConfigCurrent;
+    dmxConfigCurrent->width  = width;
+    dmxConfigCurrent->height = height;
+    dmxConfigEntry = dmxConfigAddEntry(dmxConfigEntry, dmxConfigVirtual, NULL,
+                                       dmxConfigCurrent);
+    dmxConfigECCallback(w, closure, callData);
+}
+
+static void dmxConfigDCCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    DMXConfigEntryPtr pt;
+
+    if (!dmxConfigEntry) return;
+    if (dmxConfigEntry
+        && dmxConfigEntry->type == dmxConfigVirtual
+        && dmxConfigEntry->virtual == dmxConfigCurrent) {
+        dmxConfigEntry = dmxConfigEntry->next;
+    } else {
+        for (pt = dmxConfigEntry; pt && pt->next; pt = pt->next)
+            if (pt->next->type == dmxConfigVirtual
+                && pt->next->virtual == dmxConfigCurrent) {
+                pt->next = pt->next->next;
+                break;
+            }
+    }
+    dmxConfigFreeVirtual(dmxConfigCurrent);
+    dmxConfigCurrent        = NULL;
+    dmxConfigCurrentDisplay = NULL;
+
+                                /* Make the first entry current */
+    for (pt = dmxConfigEntry; pt; pt = pt->next) {
+        if (pt->type == dmxConfigVirtual) {
+            dmxConfigCurrent = pt->virtual;
+            break;
+        }
+    }
+
+    dmxConfigSetupCnamemenu();
+    dmxConfigCanvasDraw(NULL);
+}
+
+static void dmxConfigECOkCallback(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    const char *value;
+    char       *endpt;
+
+    dmxConfigGetValueString((char **)&dmxConfigCurrent->name, ecdialog0);
+    value                    = XawDialogGetValueString(ecdialog1);
+    dmxConfigCurrent->width  = strtol(value, &endpt, 10);
+    dmxConfigCurrent->height = strtol(endpt+1, NULL, 10);
+    XtPopdown(ecpopup);
+    dmxConfigCurrentDisplay = NULL;
+    dmxConfigNewVirtual     = NULL;
+    dmxConfigSetupCnamemenu();
+    dmxConfigCanvasDraw(NULL);
+    dmxConfigNotSaved = 1;
+}
+
+static void dmxConfigECCanCallback(Widget w, XtPointer closure,
+                                   XtPointer callData)
+{
+    if (dmxConfigNewVirtual) dmxConfigDCCallback(w, closure, callData);
+    dmxConfigNewVirtual     = NULL;
+    XtPopdown(ecpopup);
+}
+
+static void dmxConfigEDCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    char buf[256];              /* RATS: Only used in XmuSnprintf */
+    
+    if (!dmxConfigCurrent || !dmxConfigCurrentDisplay) return;
+    dmxConfigSetPopupPosition(edpopup);
+    XtVaSetValues(eddialog0, XtNvalue,
+                  dmxConfigCurrentDisplay->name
+                  ? dmxConfigCurrentDisplay->name
+                  : "",
+                  NULL);
+    XmuSnprintf(buf, sizeof(buf), "%dx%d%c%d%c%d",
+                dmxConfigCurrentDisplay->scrnWidth,
+                dmxConfigCurrentDisplay->scrnHeight,
+                dmxConfigCurrentDisplay->scrnXSign < 0 ? '-' : '+',
+                dmxConfigCurrentDisplay->scrnY,
+                dmxConfigCurrentDisplay->scrnYSign < 0 ? '-' : '+',
+                dmxConfigCurrentDisplay->scrnY);
+    XtVaSetValues(eddialog1, XtNvalue, buf, NULL);
+    XmuSnprintf(buf, sizeof(buf), "@%dx%d",
+                dmxConfigCurrentDisplay->rootXOrigin,
+                dmxConfigCurrentDisplay->rootYOrigin);
+    XtVaSetValues(eddialog2, XtNvalue, buf, NULL);
+    XtPopup(edpopup, XtGrabExclusive);
+}
+
+static void dmxConfigNDCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    int width = 1280, height = 1024;
+
+    if (!dmxConfigCurrent) return;
+    if (dmxConfigCurrentDisplay) {
+        width  = dmxConfigCurrentDisplay->scrnWidth;
+        height = dmxConfigCurrentDisplay->scrnHeight;
+    }
+    dmxConfigCurrentDisplay = dmxConfigCreateDisplay(NULL, NULL, NULL,
+                                                     NULL, NULL);
+    dmxConfigNewDisplay     = dmxConfigCurrentDisplay;
+    dmxConfigCurrentDisplay->scrnWidth  = width;
+    dmxConfigCurrentDisplay->scrnHeight = height;
+    
+    dmxConfigCurrent->subentry
+        = dmxConfigAddSub(dmxConfigCurrent->subentry,
+                          dmxConfigSubDisplay(dmxConfigCurrentDisplay));
+    dmxConfigEDCallback(w, closure, callData);
+}
+
+static void dmxConfigDDCallback(Widget w, XtPointer closure,
+                                XtPointer callData)
+{
+    DMXConfigSubPtr pt;
+
+    if (!dmxConfigCurrent || !dmxConfigCurrentDisplay) return;
+                                /* First */
+    if (dmxConfigCurrent->subentry
+        && dmxConfigCurrent->subentry->type == dmxConfigDisplay
+        && dmxConfigCurrent->subentry->display == dmxConfigCurrentDisplay) {
+        dmxConfigCurrent->subentry = dmxConfigCurrent->subentry->next;
+    } else {
+        for (pt = dmxConfigCurrent->subentry; pt && pt->next; pt = pt->next)
+            if (pt->next->type == dmxConfigDisplay
+                && pt->next->display == dmxConfigCurrentDisplay) {
+                pt->next = pt->next->next;
+                break;
+            }
+    }
+    dmxConfigFreeDisplay(dmxConfigCurrentDisplay);
+    dmxConfigCurrentDisplay = NULL;
+    dmxConfigSetupCnamemenu();
+    dmxConfigCanvasDraw(NULL);
+}
+
+static void dmxConfigAboutCallback(Widget w, XtPointer closure,
+                                   XtPointer callData)
+{
+    dmxConfigSetPopupPosition(aboutpopup);
+    XtPopup(aboutpopup, XtGrabExclusive);
+}
+
+static void dmxConfigAboutOkCallback(Widget w, XtPointer closure,
+                                     XtPointer CallData)
+{
+    XtPopdown(aboutpopup);
+}
+
+static void dmxConfigQuitCallback(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    if (dmxConfigNotSaved) {
+        dmxConfigSetPopupPosition(quitpopup);
+        XtPopup(quitpopup, XtGrabExclusive);
+        return;
+    }
+    exit(0);
+}
+
+static void dmxConfigQuitOkCallback(Widget w, XtPointer closure,
+                                    XtPointer callData)
+{
+    XtPopdown(quitpopup);
+    exit(0);
+}
+
+static void dmxConfigQuitCanCallback(Widget w, XtPointer closure,
+                                     XtPointer callData)
+{
+    XtPopdown(quitpopup);
+}
+
+static void dmxConfigEDOkCallback(Widget w, XtPointer closure,
+                                  XtPointer callData)
+{
+    char *value;
+    char *endpt;
+    
+    dmxConfigNewDisplay = NULL;
+    dmxConfigGetValueString((char **)&dmxConfigCurrentDisplay->name,
+                            eddialog0);
+    value                           = XawDialogGetValueString(eddialog1);
+    if (*value == '-' || *value == '+') {
+        dmxConfigCurrentDisplay->scrnWidth  = 0;
+        dmxConfigCurrentDisplay->scrnHeight = 0;
+        endpt                               = value;
+    } else {
+        dmxConfigCurrentDisplay->scrnWidth  = strtol(value, &endpt, 10);
+        dmxConfigCurrentDisplay->scrnHeight = strtol(endpt+1, &endpt, 10);
+    }
+    if (*endpt) {
+        dmxConfigCurrentDisplay->scrnXSign  = (*endpt == '-') ? -1 : 1;
+        dmxConfigCurrentDisplay->scrnX      = strtol(endpt+1, &endpt, 10);
+        dmxConfigCurrentDisplay->scrnYSign  = (*endpt == '-') ? -1 : 1;
+        dmxConfigCurrentDisplay->scrnY      = strtol(endpt+1, NULL, 10);
+    }
+    if (dmxConfigCurrentDisplay->scrnX < 0)
+        dmxConfigCurrentDisplay->scrnX = -dmxConfigCurrentDisplay->scrnX;
+    if (dmxConfigCurrentDisplay->scrnY < 0)
+        dmxConfigCurrentDisplay->scrnY = -dmxConfigCurrentDisplay->scrnY;
+    value                                 = XawDialogGetValueString(eddialog2);
+    dmxConfigCurrentDisplay->rootXOrigin  = strtol(value+1, &endpt, 10);
+    dmxConfigCurrentDisplay->rootYOrigin  = strtol(endpt+1, NULL, 10);
+    XtPopdown(edpopup);
+    dmxConfigSetupCnamemenu();
+    dmxConfigCanvasDraw(NULL);
+    dmxConfigNotSaved = 1;
+}
+
+static void dmxConfigEDCanCallback(Widget w, XtPointer closure,
+                                   XtPointer callData)
+{
+    if (dmxConfigNewDisplay) dmxConfigDDCallback(w, closure, callData);
+    dmxConfigNewDisplay = NULL;
+    XtPopdown(edpopup);
+}
+
+static void dmxConfigOkAction(Widget w, XEvent *event,
+                              String *params, Cardinal *num_params)
+{
+    Widget p = XtParent(w);
+    Widget t;
+
+    if (p == opendialog) dmxConfigOkCallback(w, NULL, NULL);
+
+    if (p == ecdialog0) {
+        t = XtNameToWidget(ecdialog1, "value");
+        XWarpPointer(XtDisplay(t), None, XtWindow(t), 0, 0, 0, 0, 0, 10);
+    }
+    if (p == ecdialog1) dmxConfigECOkCallback(w, NULL, NULL);
+
+    if (p == eddialog0) {
+        t = XtNameToWidget(eddialog1, "value");
+        XWarpPointer(XtDisplay(t), None, XtWindow(t), 0, 0, 0, 0, 0, 10);
+    }
+    if (p == eddialog1) {
+        t = XtNameToWidget(eddialog2, "value");
+        XWarpPointer(XtDisplay(t), None, XtWindow(t), 0, 0, 0, 0, 0, 10);
+    }
+    if (p == eddialog2) dmxConfigEDOkCallback(w, NULL, NULL);
+}
+
+int main(int argc, char **argv)
+{
+    XtAppContext   appContext;
+    Widget         toplevel;
+    Widget         parent, menubox, bottombox, databox, canvasbox;
+    Widget         filebutton, helpbutton;
+    Widget         filemenu, openbutton, savebutton, quitbutton;
+    Widget         helpmenu, aboutbutton, aboutbox, abouttext, aboutok;
+    Widget         quitbox, quittext, quitok, quitcan;
+    Widget         ncbutton;
+    Widget         canbutton;
+    Widget         ecbox, ecokbutton, eccanbutton;
+    Widget         edbox, edokbutton;
+    Widget         edcanbutton;
+                                /* FIXME: add meta-i, ctrl,meta-z,v? */
+    const char     *opentrans = "<Key>Return: openOk()\n\
+                                 <Key>Linefeed: openOk()\n\
+                                 Ctrl<Key>M: openOk()\n\
+                                 Ctrl<Key>J: openOk()\n\
+                                 Ctrl<Key>O: noop()\n\
+                                 Ctrl<Key>N: noop()\n\
+                                 Ctrl<Key>P: noop()";
+    const char     *canvastrans =
+        "<Btn3Down>: placeMenu() XtMenuPopup(buttonpopup)";
+    XtActionsRec   actiontable[] = {
+        { "openOk",        dmxConfigOkAction },
+        { "placeMenu",     dmxConfigPlaceMenu },
+        { "noop",          NULL }
+    };
+
+    dmxConfigFilename = XtNewString((argc >= 2) ? argv[1] : "");
+
+    toplevel    = XtVaAppInitialize(&appContext, "XDmxconfig",
+                                    NULL, 0,
+                                    &argc, argv,
+                                    NULL,
+                                    NULL);
+
+                                /* Main boxes */
+    parent      = XtVaCreateManagedWidget("parent", formWidgetClass, toplevel,
+                                          XtNorientation, XtorientVertical,
+                                          XtNwidth, DMX_MAIN_WIDTH,
+                                          XtNheight, DMX_MAIN_HEIGHT,
+                                          NULL);
+    menubox     = XtVaCreateManagedWidget("menubox", boxWidgetClass, parent,
+                                          XtNborderWidth, 0,
+                                          XtNorientation, XtorientHorizontal,
+                                          XtNtop, XtChainTop,
+                                          NULL);
+    bottombox   = XtVaCreateManagedWidget("bottombox", formWidgetClass, parent,
+                                          XtNborderWidth, 0,
+                                          XtNfromVert, menubox,
+                                          XtNorientation, XtorientHorizontal,
+                                          NULL);
+    databox     = XtVaCreateManagedWidget("databox", formWidgetClass,
+                                          bottombox,
+                                          XtNborderWidth, 0,
+                                          XtNhorizDistance, 0,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNheight, DMX_DATA_HEIGHT,
+                                          XtNleft, XtChainLeft,
+                                          XtNorientation, XtorientVertical,
+                                          NULL);
+
+                                /* Data */
+    cnamebox    = XtVaCreateManagedWidget("cnamebox", menuButtonWidgetClass,
+                                          databox,
+                                          XtNtop, XtChainTop,
+                                          XtNjustify, XtJustifyLeft,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNlabel, "",
+                                          XtNmenuName, "cnamemenu",
+                                          NULL);
+    cdimbox     = XtVaCreateManagedWidget("cdimbox", labelWidgetClass,
+                                          databox,
+                                          XtNfromVert, cnamebox,
+                                          XtNjustify, XtJustifyLeft,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNlabel, "",
+                                          NULL);
+    namebox     = XtVaCreateManagedWidget("namebox", labelWidgetClass, databox,
+                                          XtNfromVert, cdimbox,
+                                          XtNjustify, XtJustifyLeft,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNlabel, "",
+                                          NULL);
+    dimbox      = XtVaCreateManagedWidget("dimbox", labelWidgetClass,
+                                          databox,
+                                          XtNfromVert, namebox,
+                                          XtNjustify, XtJustifyLeft,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNlabel, "",
+                                          NULL);
+    rtbox       = XtVaCreateManagedWidget("rtbox", labelWidgetClass,
+                                          databox,
+                                          XtNfromVert, dimbox,
+                                          XtNjustify, XtJustifyLeft,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNlabel, "",
+                                          NULL);
+    origbox     = XtVaCreateManagedWidget("origbox", labelWidgetClass,
+                                          databox,
+                                          XtNfromVert, rtbox,
+                                          XtNjustify, XtJustifyLeft,
+                                          XtNwidth, DMX_DATA_WIDTH,
+                                          XtNlabel, "",
+                                          NULL);
+
+                                /* Canvas */
+    canvasbox   = XtVaCreateManagedWidget("canvasbox", boxWidgetClass,
+                                          bottombox,
+                                          XtNborderWidth, 0,
+                                          XtNwidth, DMX_CANVAS_WIDTH,
+                                          XtNheight, DMX_CANVAS_HEIGHT,
+                                          XtNfromHoriz, databox,
+                                          NULL);
+
+    canvas      = XtVaCreateManagedWidget("canvas", canvasWidgetClass,
+                                          canvasbox,
+                                          XtNwidth, DMX_CANVAS_WIDTH,
+                                          XtNheight, DMX_CANVAS_HEIGHT,
+                                          NULL);
+
+    
+                                /* Main menu buttons */
+    filebutton  = XtVaCreateManagedWidget("File", menuButtonWidgetClass,
+                                          menubox,
+                                          XtNmenuName, "filemenu",
+                                          NULL);
+    helpbutton  = XtVaCreateManagedWidget("Help", menuButtonWidgetClass,
+                                          menubox,
+                                          XtNmenuName, "helpmenu",
+                                          NULL);
+
+    
+                                /* File submenu buttons */
+    filemenu     = XtVaCreatePopupShell("filemenu", simpleMenuWidgetClass,
+                                        filebutton, NULL);
+    openbutton   = XtVaCreateManagedWidget("Open File", smeBSBObjectClass,
+                                           filemenu, NULL);
+    savebutton   = XtVaCreateManagedWidget("Save File", smeBSBObjectClass,
+                                           filemenu,
+                                           NULL);
+    ncbutton     = XtVaCreateManagedWidget("New Global", smeBSBObjectClass,
+                                           filemenu, NULL);
+    ecbutton     = XtVaCreateManagedWidget("Edit Global", smeBSBObjectClass,
+                                           filemenu,
+                                           NULL);
+    dcbutton     = XtVaCreateManagedWidget("Delete Global", smeBSBObjectClass,
+                                           filemenu,
+                                           NULL);
+    ndbutton0    = XtVaCreateManagedWidget("New Display", smeBSBObjectClass,
+                                           filemenu,
+                                           NULL);
+    quitbutton   = XtVaCreateManagedWidget("Quit", smeBSBObjectClass,
+                                           filemenu, NULL);
+
+                                /* Help submenu button */
+    helpmenu     = XtVaCreatePopupShell("helpmenu", simpleMenuWidgetClass,
+                                        helpbutton, NULL);
+    aboutbutton  = XtVaCreateManagedWidget("About", smeBSBObjectClass,
+                                           helpmenu, NULL);
+    
+                                /* Open popup */
+    openpopup    = XtVaCreatePopupShell("openpopup", transientShellWidgetClass,
+                                        toplevel, NULL);
+    opendialog   = XtVaCreateManagedWidget("opendialog", dialogWidgetClass,
+                                           openpopup,
+                                           XtNlabel, "Filename: ",
+                                           XtNvalue, dmxConfigFilename,
+                                           NULL);
+    okbutton     = XtVaCreateManagedWidget("Open", commandWidgetClass,
+                                           opendialog, NULL);
+    canbutton    = XtVaCreateManagedWidget("Cancel", commandWidgetClass,
+                                           opendialog, NULL);
+
+                                /* EC popup */
+    ecpopup      = XtVaCreatePopupShell("ecpopup", transientShellWidgetClass,
+                                        toplevel, NULL);
+    ecbox        = XtVaCreateManagedWidget("ecbox", boxWidgetClass,
+                                           ecpopup, NULL);
+    ecdialog0    = XtVaCreateManagedWidget("ecdialog0", dialogWidgetClass,
+                                           ecbox,
+                                           XtNlabel, "Name:              ",
+                                           XtNvalue, "",
+                                           NULL);
+    ecdialog1    = XtVaCreateManagedWidget("ecdialog1", dialogWidgetClass,
+                                           ecbox,
+                                           XtNlabel, "Dimension:         ",
+                                           XtNvalue, "",
+                                           NULL);
+    ecokbutton   = XtVaCreateManagedWidget("OK", commandWidgetClass,
+                                           ecbox, NULL);
+    eccanbutton  = XtVaCreateManagedWidget("Cancel", commandWidgetClass,
+                                           ecbox, NULL);
+
+                                /* ED popup */
+    edpopup      = XtVaCreatePopupShell("edpopup", transientShellWidgetClass,
+                                        toplevel, NULL);
+    edbox        = XtVaCreateManagedWidget("edbox", boxWidgetClass,
+                                           edpopup, NULL);
+    eddialog0    = XtVaCreateManagedWidget("eddialog0", dialogWidgetClass,
+                                           edbox,
+                                           XtNlabel, "Display Name:      ",
+                                           XtNvalue, "",
+                                           NULL);
+    eddialog1    = XtVaCreateManagedWidget("eddialog1", dialogWidgetClass,
+                                           edbox,
+                                           XtNlabel, "Geometry:          ",
+                                           XtNvalue, "",
+                                           NULL);
+    eddialog2    = XtVaCreateManagedWidget("eddialog2", dialogWidgetClass,
+                                           edbox,
+                                           XtNlabel, "Offset:            ",
+                                           XtNvalue, "",
+                                           NULL);
+    edokbutton   = XtVaCreateManagedWidget("OK", commandWidgetClass,
+                                           edbox, NULL);
+    edcanbutton  = XtVaCreateManagedWidget("Cancel", commandWidgetClass,
+                                           edbox, NULL);
+
+                                /* About popup */
+    aboutpopup   = XtVaCreatePopupShell("aboutpopup",transientShellWidgetClass,
+                                        toplevel, NULL);
+    aboutbox     = XtVaCreateManagedWidget("aboutbox", boxWidgetClass,
+                                           aboutpopup, NULL);
+    abouttext    = XtVaCreateManagedWidget("abouttext", labelWidgetClass,
+                                           aboutbox,
+                                           XtNlabel, DMX_INFO,
+                                           NULL);
+    aboutok      = XtVaCreateManagedWidget("OK", commandWidgetClass,
+                                           aboutbox, NULL);
+
+                                /* Quit popup */
+    quitpopup    = XtVaCreatePopupShell("quitpopup",transientShellWidgetClass,
+                                        toplevel, NULL);
+    quitbox      = XtVaCreateManagedWidget("quitbox", boxWidgetClass,
+                                           quitpopup, NULL);
+    quittext     = XtVaCreateManagedWidget("quittext", labelWidgetClass,
+                                           quitbox,
+                                           XtNlabel,
+                                           "Changes to the configuration\n"
+                                           "been made that have not yet\n"
+                                           "been saved.  Do you want to\n"
+                                           "quit without saving?",
+                                           NULL);
+    quitok       = XtVaCreateManagedWidget("Quit WITHOUT Saving",
+                                           commandWidgetClass,
+                                           quitbox, NULL);
+    quitcan      = XtVaCreateManagedWidget("Continue Editing",
+                                           commandWidgetClass,
+                                           quitbox, NULL);
+
+                                /* Button popup */
+    buttonpopup  = XtVaCreatePopupShell("buttonpopup", simpleMenuWidgetClass,
+                                        toplevel, NULL);
+    ndbutton1    = XtVaCreateManagedWidget("New Display", smeBSBObjectClass,
+                                           buttonpopup,
+                                           NULL);
+    edbutton     = XtVaCreateManagedWidget("Edit Display", smeBSBObjectClass,
+                                           buttonpopup,
+                                           NULL);
+    ddbutton     = XtVaCreateManagedWidget("Delete Display", smeBSBObjectClass,
+                                           buttonpopup,
+                                           NULL);
+
+                                /* Callbacks */
+    XtAddCallback(openbutton,  XtNcallback, dmxConfigOpenCallback,       NULL);
+    XtAddCallback(savebutton,  XtNcallback, dmxConfigSaveCallback,       NULL);
+    XtAddCallback(okbutton,    XtNcallback, dmxConfigOkCallback,         NULL);
+    XtAddCallback(canbutton,   XtNcallback, dmxConfigCanCallback,        NULL);
+    
+    XtAppAddActions(appContext, actiontable, XtNumber(actiontable));
+    XtOverrideTranslations(canvas, XtParseTranslationTable(canvastrans));
+    XtOverrideTranslations(XtNameToWidget(opendialog, "value"),
+                                          XtParseTranslationTable(opentrans));
+    XtOverrideTranslations(XtNameToWidget(ecdialog0, "value"),
+                                          XtParseTranslationTable(opentrans));
+    XtOverrideTranslations(XtNameToWidget(ecdialog1, "value"),
+                                          XtParseTranslationTable(opentrans));
+    XtOverrideTranslations(XtNameToWidget(eddialog0, "value"),
+                                          XtParseTranslationTable(opentrans));
+    XtOverrideTranslations(XtNameToWidget(eddialog1, "value"),
+                                          XtParseTranslationTable(opentrans));
+    XtOverrideTranslations(XtNameToWidget(eddialog2, "value"),
+                                          XtParseTranslationTable(opentrans));
+    
+    XtAddCallback(ncbutton,    XtNcallback, dmxConfigNCCallback,         NULL);
+    XtAddCallback(ecbutton,    XtNcallback, dmxConfigECCallback,         NULL);
+    XtAddCallback(ecokbutton,  XtNcallback, dmxConfigECOkCallback,       NULL);
+    XtAddCallback(eccanbutton, XtNcallback, dmxConfigECCanCallback,      NULL);
+    XtAddCallback(dcbutton,    XtNcallback, dmxConfigDCCallback,         NULL);
+
+    XtAddCallback(ndbutton0,   XtNcallback, dmxConfigNDCallback,         NULL);
+    XtAddCallback(ndbutton1,   XtNcallback, dmxConfigNDCallback,         NULL);
+    XtAddCallback(edbutton,    XtNcallback, dmxConfigEDCallback,         NULL);
+    XtAddCallback(ddbutton,    XtNcallback, dmxConfigDDCallback,         NULL);
+    XtAddCallback(edokbutton,  XtNcallback, dmxConfigEDOkCallback,       NULL);
+    XtAddCallback(edcanbutton, XtNcallback, dmxConfigEDCanCallback,      NULL);
+
+    XtAddCallback(aboutbutton, XtNcallback, dmxConfigAboutCallback,      NULL);
+    XtAddCallback(aboutok,     XtNcallback, dmxConfigAboutOkCallback,    NULL);
+    XtAddCallback(quitok,      XtNcallback, dmxConfigQuitOkCallback,     NULL);
+    XtAddCallback(quitcan,     XtNcallback, dmxConfigQuitCanCallback,    NULL);
+    
+    XtAddCallback(quitbutton,  XtNcallback, dmxConfigQuitCallback,       NULL);
+    
+    XtAddCallback(canvas, XtNcallback,             dmxConfigCanvasInput, NULL);
+    XtAddCallback(canvas, XtNcanvasExposeCallback, dmxConfigCanvasExpose,NULL);
+    XtAddCallback(canvas, XtNcanvasResizeCallback, dmxConfigCanvasResize,NULL);
+
+    if (dmxConfigFilename) dmxConfigReadFile();
+    
+    XtRealizeWidget(toplevel);
+    dmxConfigCopystrings();
+    dmxConfigSetupCnamemenu();
+    XtAppMainLoop(appContext);
+    return 0;
+}
diff --git a/hw/dmx/config/xdmxconfig.man b/hw/dmx/config/xdmxconfig.man
new file mode 100644
index 000000000..dcceea0e3
--- /dev/null
+++ b/hw/dmx/config/xdmxconfig.man
@@ -0,0 +1,63 @@
+.\" $XFree86$
+.\" Copyright 2002 Red Hat Inc., Durham, North Carolina.
+.\" All Rights Reserved.
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation on the rights to use, copy, modify, merge,
+.\" publish, distribute, sublicense, and/or sell copies of the Software,
+.\" and to permit persons to whom the Software is furnished to do so,
+.\" subject to the following conditions:
+.\"
+.\" The above copyright notice and this permission notice (including the
+.\" next paragraph) shall be included in all copies or substantial
+.\" portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+.\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+.\" BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+.\" ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+.\" CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+.\" SOFTWARE.
+.\"
+.\" Authors:
+.\"   Rickard E. (Rik) Faith <faith@redhat.com>
+.\"
+.TH xdmxconfig 1 __vendorversion__
+.SH NAME
+xdmxconfig - a graphical configuration tool for Xdmx configuration files
+.SH SYNOPSIS
+.B xdmxconfig
+[filename]
+.SH DESCRIPTION
+.I xdmxconfig
+reads, edits, and writes configuration files for the Xdmx server.  The
+grammar for the configuration file is specified in the Xdmx(1) manual
+page.
+.PP
+To start from scratch, create a "New Global" and specify the name and
+overall dimensions for the configuration.  Then use "New Display" to
+enter more displays.
+.PP
+If there is more than one configuration, the configuration name button
+will bring up a selection menu.
+.PP
+In the right-hand pannel, the left mouse button will move the
+highlighted display at "tool resolution"; the middle mouse button will
+move the highlighted display by a single pixel (at "wall resolution");
+and the right mouse button will bring up a menu allowing the highlighted
+display to be edited or deleted.  The arrow keys will also move the
+highlighted display by a single pixel.
+.SH BUGS
+Currently, entries with the
+.B wall
+keyword are not editable, but will be preserved in the new output file.
+The tool will quit when requested by the user, even if a configuration
+file has not been written out (i.e., without warning).  The menu
+interaction should be improved (menu entries that don't currently work
+should be greyed-out, for example).  The Help button does not work.
+.SH "SEE ALSO"
+Xdmx(1), vdltodmx(1)
-- 
cgit v1.2.3