Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@590 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/unix/tx/Makefile.in b/unix/tx/Makefile.in
new file mode 100644
index 0000000..89c30b1
--- /dev/null
+++ b/unix/tx/Makefile.in
@@ -0,0 +1,17 @@
+
+SRCS = TXWindow.cxx TXScrollbar.cxx TXViewport.cxx TXImage.cxx TXMenu.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @X_CFLAGS@ # X_CFLAGS are really CPPFLAGS
+
+library = libtx.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/unix/tx/TXButton.h b/unix/tx/TXButton.h
new file mode 100644
index 0000000..b747279
--- /dev/null
+++ b/unix/tx/TXButton.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXButton.h
+//
+// A TXButton is a clickable button with some text in it.  The button must be
+// big enough to contain the text - if not then it will be resized
+// appropriately.
+//
+
+#ifndef __TXBUTTON_H__
+#define __TXBUTTON_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXButtonCallback's buttonActivate() method is called when a button is
+// activated.
+class TXButton;
+class TXButtonCallback {
+public:
+  virtual void buttonActivate(TXButton* button)=0;
+};
+
+
+class TXButton : public TXWindow, public TXEventHandler {
+public:
+
+  TXButton(Display* dpy_, const char* text_, TXButtonCallback* cb_=0,
+           TXWindow* parent_=0, int w=1, int h=1)
+    : TXWindow(dpy_, w, h, parent_), cb(cb_), down(false),
+      disabled_(false)
+  {
+    setEventHandler(this);
+    setText(text_);
+    gc = XCreateGC(dpy, win(), 0, 0);
+    XSetFont(dpy, gc, defaultFont);
+    addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask);
+  }
+
+  virtual ~TXButton() {
+    XFreeGC(dpy, gc);
+  }
+
+  // setText() changes the text in the button.
+  void setText(const char* text_) {
+    text.buf = rfb::strDup(text_);
+    int textWidth = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+    int textHeight = (defaultFS->ascent + defaultFS->descent);
+    int newWidth = __rfbmax(width(), textWidth + xPad*2 + bevel*2);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2);
+    if (width() < newWidth || height() < newHeight) {
+      resize(newWidth, newHeight);
+    }
+  }
+
+  // disabled() sets or queries the disabled state of the checkbox.  A disabled
+  // checkbox cannot be changed via the user interface.
+  void disabled(bool b) { disabled_ = b; paint(); }
+  bool disabled() { return disabled_; }
+
+private:
+
+  void paint() {
+    int tw = XTextWidth(defaultFS, text.buf, strlen(text.buf));
+    int startx = (width() - tw) / 2;
+    int starty = (height() + defaultFS->ascent - defaultFS->descent) / 2;
+    if (down || disabled_) {
+      drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, darkBg,lightBg);
+      startx++; starty++;
+    } else {
+      drawBevel(gc, 0, 0, width(), height(), bevel, defaultBg, lightBg,darkBg);
+    }
+
+    XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+    XDrawString(dpy, win(), gc, startx, starty, text.buf, strlen(text.buf));
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+    case ButtonPress:
+      if (!disabled_) {
+        down = true;
+        paint();
+      }
+      break;
+    case ButtonRelease:
+      if (!down) break;
+      down = false;
+      paint();
+      if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+          ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+        if (cb) cb->buttonActivate(this);
+      }
+      break;
+    }
+  }
+
+  GC gc;
+  rfb::CharArray text;
+  TXButtonCallback* cb;
+  bool down;
+  bool disabled_;
+};
+
+#endif
diff --git a/unix/tx/TXCheckbox.h b/unix/tx/TXCheckbox.h
new file mode 100644
index 0000000..0880b38
--- /dev/null
+++ b/unix/tx/TXCheckbox.h
@@ -0,0 +1,142 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXCheckbox.h
+//
+// A TXCheckbox has a box which may be "checked" with some text next to it.
+// The checkbox window must be big enough to contain the text - if not then it
+// will be resized appropriately.
+//
+// There are two styles of checkbox: the normal style which uses a tick in a
+// square box, and the radio style which uses a dot inside a circle.  The
+// default behaviour when clicking on the checkbox is to toggle it on or off,
+// but this behaviour can be changed by the callback object.  In particular to
+// get radiobutton behaviour, the callback must ensure that only one of a set
+// of radiobuttons is selected.
+//
+
+#ifndef __TXCHECKBOX_H__
+#define __TXCHECKBOX_H__
+
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+// TXCheckboxCallback's checkboxSelect() method is called when the state of a
+// checkbox changes.
+class TXCheckbox;
+class TXCheckboxCallback {
+public:
+  virtual void checkboxSelect(TXCheckbox* checkbox)=0;
+};
+
+
+class TXCheckbox : public TXWindow, public TXEventHandler {
+public:
+  TXCheckbox(Display* dpy_, const char* text_, TXCheckboxCallback* cb_,
+             bool radio_=false, TXWindow* parent_=0, int w=1, int h=1)
+    : TXWindow(dpy_, w, h, parent_), cb(cb_), text(0),
+      boxSize(radio_ ? 12 : 13), boxPad(4),
+      checked_(false), disabled_(false), radio(radio_)
+  {
+    setEventHandler(this);
+    setText(text_);
+    gc = XCreateGC(dpy, win(), 0, 0);
+    XSetFont(dpy, gc, defaultFont);
+    addEventMask(ExposureMask| ButtonPressMask | ButtonReleaseMask);
+  }
+
+  virtual ~TXCheckbox() {
+    XFreeGC(dpy, gc);
+    if (text) free(text);
+  }
+
+  // setText() changes the text in the checkbox.
+  void setText(const char* text_) {
+    if (text) free(text);
+    text = strdup((char*)text_);
+    int textWidth = XTextWidth(defaultFS, text, strlen(text));
+    int textHeight = (defaultFS->ascent + defaultFS->descent);
+    int newWidth = __rfbmax(width(), textWidth + xPad*2 + boxPad*2 + boxSize);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2);
+    if (width() < newWidth || height() < newHeight) {
+      resize(newWidth, newHeight);
+    }
+  }
+
+  // checked() sets or queries the state of the checkbox
+  void checked(bool b) { checked_ = b; paint(); }
+  bool checked() { return checked_; }
+
+  // disabled() sets or queries the disabled state of the checkbox.  A disabled
+  // checkbox cannot be changed via the user interface.
+  void disabled(bool b) { disabled_ = b; paint(); }
+  bool disabled() { return disabled_; }
+
+private:
+  void paint() {
+    if (disabled_)
+      drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+                bevel, disabledBg, darkBg, lightBg, radio);
+    else
+      drawBevel(gc, xPad + boxPad, (height() - boxSize) / 2, boxSize, boxSize,
+                bevel, enabledBg, darkBg, lightBg, radio);
+    XSetBackground(dpy, gc, disabled_ ? disabledBg : enabledBg);
+    XSetForeground(dpy, gc, disabled_ ? disabledFg : defaultFg);
+    if (checked_) {
+      Pixmap icon = radio ? dot : tick;
+      int iconSize = radio ? dotSize : tickSize;
+      XCopyPlane(dpy, icon, win(), gc, 0, 0, iconSize, iconSize,
+                 xPad + boxPad + (boxSize - iconSize) / 2,
+                 (height() - iconSize) / 2, 1);
+    }
+    XDrawString(dpy, win(), gc, xPad + boxSize + boxPad*2,
+                (height() + defaultFS->ascent - defaultFS->descent) / 2,
+                text, strlen(text));
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+    case ButtonPress:
+      break;
+    case ButtonRelease:
+      if (ev->xbutton.x >= 0 && ev->xbutton.x < width() &&
+          ev->xbutton.y >= 0 && ev->xbutton.y < height()) {
+        if (!disabled_) {
+          checked_ = !checked_;
+          if (cb) cb->checkboxSelect(this);
+          paint();
+        }
+      }
+      break;
+    }
+  }
+
+  TXCheckboxCallback* cb;
+  GC gc;
+  char* text;
+  int boxSize;
+  int boxPad;
+  bool checked_;
+  bool disabled_;
+  bool radio;
+};
+
+#endif
diff --git a/unix/tx/TXDialog.h b/unix/tx/TXDialog.h
new file mode 100644
index 0000000..c8d601c
--- /dev/null
+++ b/unix/tx/TXDialog.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXDialog.h
+//
+// A TXDialog is a pop-up dialog window.  The dialog can be made visible by
+// calling its show() method.  Dialogs can be modal or non-modal.  For a modal
+// dialog box, the show() method only returns when the dialog box has been
+// dismissed.  For a non-modal dialog box, the show() method returns
+// immediately.
+//
+
+#ifndef __TXDIALOG_H__
+#define __TXDIALOG_H__
+
+#include "TXWindow.h"
+#include <errno.h>
+
+// XXX Lynx/OS 2.3: protos for bzero(), select()
+#ifdef Lynx
+#include <sys/proto.h>
+#endif
+
+class TXDialog : public TXWindow, public TXDeleteWindowCallback {
+public:
+  TXDialog(Display* dpy, int width, int height, const char* name,
+           bool modal_=false)
+    : TXWindow(dpy, width, height), done(false), ok(false), modal(modal_)
+  {
+    toplevel(name, this);
+    resize(width, height);
+  }
+
+  virtual ~TXDialog() {}
+
+  // show() makes the dialog visible.  For a modal dialog box, this processes X
+  // events until the done flag has been set, after which it returns the value
+  // of the ok flag.  For a non-modal dialog box it always returns true
+  // immediately.
+  bool show() {
+    ok = false;
+    done = false;
+    initDialog();
+    raise();
+    map();
+    if (modal) {
+      while (true) {
+        TXWindow::handleXEvents(dpy);
+        if (done) {
+          return ok;
+        }
+        fd_set rfds;
+        FD_ZERO(&rfds);
+        FD_SET(ConnectionNumber(dpy), &rfds);
+        int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+        if (n < 0) throw rdr::SystemException("select",errno);
+      }
+    }
+    return true;
+  }
+
+  // initDialog() can be overridden in a derived class.  Typically it is used
+  // to make sure that checkboxes have the right state, etc.
+  virtual void initDialog() {}
+
+  // resize() is overidden here to re-center the dialog
+  void resize(int w, int h) {
+    TXWindow::resize(w,h);
+    int dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+    int dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
+    setUSPosition((dpyWidth - width() - 10) / 2, (dpyHeight - height() - 30) / 2);
+  }    
+
+protected:
+  virtual void deleteWindow(TXWindow* w) {
+    ok = false;
+    done = true;
+    unmap();
+  }
+
+  bool done;
+  bool ok;
+  bool modal;
+};
+
+#endif
diff --git a/unix/tx/TXEntry.h b/unix/tx/TXEntry.h
new file mode 100644
index 0000000..1785a5f
--- /dev/null
+++ b/unix/tx/TXEntry.h
@@ -0,0 +1,191 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXEntry.h
+//
+// A TXEntry allows you to enter a single line of text in a window.  The entry
+// must be tall enough to contain a line of text - if not then it will be
+// resized appropriately.  If the passwd argument to the constructor is true,
+// then the text in the entry will be replaced by asterisks on the screen.
+//
+
+#ifndef __TXENTRY_H__
+#define __TXENTRY_H__
+
+#include "TXWindow.h"
+#include <X11/keysym.h>
+
+#ifndef XK_ISO_Left_Tab
+#define	XK_ISO_Left_Tab					0xFE20
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete					0xFF9F
+#endif
+
+// TXEntryCallback's entryCallback() method is called when one of three special
+// key presses have happened: Enter/Return, forward tab, or backward tab.
+class TXEntry;
+class TXEntryCallback {
+public:
+  enum Detail { ENTER, NEXT_FOCUS, PREV_FOCUS };
+  virtual void entryCallback(TXEntry* entry, Detail detail, Time time)=0;
+};
+
+
+class TXEntry : public TXWindow, public TXEventHandler {
+public:
+
+  TXEntry(Display* dpy_, TXEntryCallback* cb_=0,
+          TXWindow* parent_=0, bool passwd_=false, int w=1, int h=1)
+    : TXWindow(dpy_, w, h, parent_), cb(cb_),
+      passwd(passwd_), disabled_(false), gotFocus(false)
+  {
+    setEventHandler(this);
+    gc = XCreateGC(dpy, win(), 0, 0);
+    addEventMask(ExposureMask | KeyPressMask | FocusChangeMask
+                 | ButtonPressMask);
+    text[0] = 0;
+    int textHeight = (defaultFS->ascent + defaultFS->descent);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2);
+    if (height() < newHeight) {
+      resize(width(), newHeight);
+    }
+  }
+
+  virtual ~TXEntry() {
+    XFreeGC(dpy, gc);
+    // overwrite memory used to store password - not critical, but can avoid
+    // accidental exposure of a password in uninitialised memory.
+    if (passwd)
+      memset(text, 0, maxLen);
+  }
+
+  // getText() gets the text in the entry.
+  const char* getText() { return text; }
+
+  // setText() sets the text in the entry.
+  void setText(const char* text_) {
+    strncpy(text, text_, maxLen-1);
+    text[maxLen-1] = 0;
+    paint();
+  }
+
+  // disabled() sets or queries the disabled state of the entry.  A disabled
+  // entry cannot have text entered into it.
+  void disabled(bool b) { disabled_ = b; paint(); }
+  bool disabled() { return disabled_; }
+
+private:
+  void paint() {
+    if (disabled_)
+      drawBevel(gc, 0, 0, width(), height(), bevel, disabledBg,darkBg,lightBg);
+    else
+      drawBevel(gc, 0, 0, width(), height(), bevel, enabledBg, darkBg,lightBg);
+    char* str = text;
+    char stars[maxLen];
+    if (passwd) {
+      int i;
+      for (i = 0; i < (int)strlen(text); i++) stars[i] = '*';
+      stars[i] = 0;
+      str = stars;
+    }
+    int tw = XTextWidth(defaultFS, str, strlen(str));
+    int startx = bevel + xPad;
+    if (startx + tw > width() - 2*bevel) {
+      startx = width() - 2*bevel - tw;
+    }
+    XDrawString(dpy, win(), defaultGC, startx,
+                (height() + defaultFS->ascent - defaultFS->descent) / 2,
+                str, strlen(str));
+    if (!disabled_ && gotFocus)
+      XDrawLine(dpy, win(), defaultGC, startx+tw,
+                (height() - defaultFS->ascent - defaultFS->descent) / 2,
+                startx+tw,
+                (height() + defaultFS->ascent + defaultFS->descent) / 2);
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+
+    case FocusIn:
+      gotFocus = true;
+      paint();
+      break;
+
+    case FocusOut:
+      gotFocus = false;
+      paint();
+      break;
+
+    case ButtonPress:
+      if (!disabled_)
+        XSetInputFocus(dpy, win(), RevertToParent, ev->xbutton.time);
+      break;
+
+    case KeyPress:
+      {
+        if (disabled_ || !gotFocus) break;
+        KeySym keysym;
+        XComposeStatus compose;
+        char buf[10];
+        int count = XLookupString(&ev->xkey, buf, 10, &keysym, &compose);
+        if (count >= 1 && buf[0] >= ' ' && buf[0] <= '~') {
+          if (strlen(text) + count >= maxLen) {
+            XBell(dpy, 0);
+          } else {
+            strncat(text, buf, count);
+            paint();
+          }
+        } else if (keysym == XK_BackSpace || keysym == XK_Delete ||
+                   keysym == XK_KP_Delete) {
+          if (strlen(text) > 0) {
+            text[strlen(text)-1] = 0;
+            paint();
+          }
+        } else if (keysym == XK_Return || keysym == XK_KP_Enter ||
+                   keysym == XK_Linefeed) {
+          if (cb) cb->entryCallback(this, TXEntryCallback::ENTER,
+                                    ev->xkey.time);
+        } else if ((keysym == XK_Tab || keysym == XK_KP_Tab)
+                   && !(ev->xkey.state & ShiftMask))
+        {
+          if (cb) cb->entryCallback(this, TXEntryCallback::NEXT_FOCUS,
+                                    ev->xkey.time);
+        } else if (((keysym == XK_Tab || keysym == XK_KP_Tab)
+                    && (ev->xkey.state & ShiftMask))
+                   || keysym == XK_ISO_Left_Tab)
+        {
+          if (cb) cb->entryCallback(this, TXEntryCallback::PREV_FOCUS,
+                                    ev->xkey.time);
+        }
+      }
+    }
+  }
+  GC gc;
+  enum { maxLen = 256 };
+  char text[maxLen];
+  TXEntryCallback* cb;
+  bool passwd;
+  bool disabled_;
+  bool gotFocus;
+};
+
+#endif
diff --git a/unix/tx/TXImage.cxx b/unix/tx/TXImage.cxx
new file mode 100644
index 0000000..5fa6828
--- /dev/null
+++ b/unix/tx/TXImage.cxx
@@ -0,0 +1,353 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXImage.cxx
+//
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <list>
+#include <rfb/TransImageGetter.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXImage.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("TXImage");
+
+TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_)
+  : xim(0), dpy(d), vis(vis_), depth(depth_), tig(0), cube(0)
+{
+#ifdef HAVE_MITSHM
+  shminfo = 0;
+#endif
+  width_ = width;
+  height_ = height;
+  for (int i = 0; i < 256; i++)
+    colourMap[i].r = colourMap[i].g = colourMap[i].b = 0;
+
+  if (!vis)
+    vis = DefaultVisual(dpy,DefaultScreen(dpy));
+  if (!depth)
+    depth = DefaultDepth(dpy,DefaultScreen(dpy));
+
+  createXImage();
+  getNativePixelFormat(vis, depth);
+  colourmap = this;
+  format.bpp = 0;  // just make it different to any valid format, so that...
+  setPF(nativePF); // ...setPF() always works
+}
+
+TXImage::~TXImage()
+{
+  if (data != (rdr::U8*)xim->data) delete [] data;
+  destroyXImage();
+  delete tig;
+  delete cube;
+}
+
+void TXImage::resize(int w, int h)
+{
+  if (w == width() && h == height()) return;
+
+  int oldStrideBytes = getStride() * (format.bpp/8);
+  int rowsToCopy = __rfbmin(h, height());
+  int bytesPerRow = __rfbmin(w, width()) * (format.bpp/8);
+  rdr::U8* oldData = 0;
+  bool allocData = false;
+
+  if (data != (rdr::U8*)xim->data) {
+    oldData = (rdr::U8*)data;
+    allocData = true;
+  } else {
+    oldData = new rdr::U8[xim->bytes_per_line * height()];
+    memcpy(oldData, xim->data, xim->bytes_per_line * height());
+  }
+
+  destroyXImage();
+  width_ = w;
+  height_ = h;
+  createXImage();
+
+  if (allocData)
+    data = new rdr::U8[width() * height() * (format.bpp/8)];
+  else
+    data = (rdr::U8*)xim->data;
+
+  int newStrideBytes = getStride() * (format.bpp/8);
+  for (int i = 0; i < rowsToCopy; i++)
+    memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i,
+           bytesPerRow);
+  delete [] oldData;
+}
+
+void TXImage::setPF(const PixelFormat& newPF)
+{
+  if (newPF.equal(format)) return;
+  format = newPF;
+
+  if (data != (rdr::U8*)xim->data) delete [] data;
+  delete tig;
+  tig = 0;
+
+  if (format.equal(nativePF) && format.trueColour) {
+    data = (rdr::U8*)xim->data;
+  } else {
+    data = new rdr::U8[width() * height() * (format.bpp/8)];
+    tig = new TransImageGetter();
+    tig->init(this, nativePF, 0, cube);
+  }
+}
+
+int TXImage::getStride() const
+{
+  if (data == (rdr::U8*)xim->data)
+    return xim->bytes_per_line / (xim->bits_per_pixel / 8);
+  else
+    return width();
+}
+
+void TXImage::put(Window win, GC gc, const rfb::Rect& r)
+{
+  if (r.is_empty()) return;
+  int x = r.tl.x;
+  int y = r.tl.y;
+  int w = r.width();
+  int h = r.height();
+  if (data != (rdr::U8*)xim->data) {
+    rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line
+                             + x * (xim->bits_per_pixel / 8));
+    tig->getImage(ximDataStart, r,
+                  xim->bytes_per_line / (xim->bits_per_pixel / 8));
+  }
+#ifdef HAVE_MITSHM
+  if (usingShm()) {
+    XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False);
+    return;
+  }
+#endif
+  XPutImage(dpy, win, gc, xim, x, y, x, y, w, h);
+}
+
+void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+  for (int i = 0; i < nColours; i++) {
+    colourMap[firstColour+i].r = rgbs[i*3];
+    colourMap[firstColour+i].g = rgbs[i*3+1];
+    colourMap[firstColour+i].b = rgbs[i*3+2];
+  }
+}
+
+void TXImage::updateColourMap()
+{
+  tig->setColourMapEntries(0, 0, 0);
+}
+
+void TXImage::lookup(int index, int* r, int* g, int* b)
+{
+  *r = colourMap[index].r;
+  *g = colourMap[index].g;
+  *b = colourMap[index].b;
+}
+
+#ifdef HAVE_MITSHM
+static bool caughtError = false;
+
+static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
+{
+  caughtError = true;
+  return 0;
+}
+
+class TXImageCleanup {
+public:
+  std::list<TXImage*> images;
+  ~TXImageCleanup() {
+    while (!images.empty())
+      delete images.front();
+  }
+};
+
+static TXImageCleanup imageCleanup;
+#endif
+
+void TXImage::createXImage()
+{
+#ifdef HAVE_MITSHM
+  int major, minor;
+  Bool pixmaps;
+
+  if (XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
+    shminfo = new XShmSegmentInfo;
+
+    xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
+                          0, shminfo, width(), height());
+
+    if (xim) {
+      shminfo->shmid = shmget(IPC_PRIVATE,
+                              xim->bytes_per_line * xim->height,
+                              IPC_CREAT|0777);
+
+      if (shminfo->shmid != -1) {
+        shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+        if (shminfo->shmaddr != (char *)-1) {
+
+          shminfo->readOnly = False;
+
+          XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
+          XShmAttach(dpy, shminfo);
+          XSync(dpy, False);
+          XSetErrorHandler(oldHdlr);
+
+          if (!caughtError) {
+            vlog.debug("Using shared memory XImage");
+            imageCleanup.images.push_back(this);
+            return;
+          }
+
+          shmdt(shminfo->shmaddr);
+        } else {
+          vlog.error("shmat failed");
+          perror("shmat");
+        }
+
+        shmctl(shminfo->shmid, IPC_RMID, 0);
+      } else {
+        vlog.error("shmget failed");
+        perror("shmget");
+      }
+
+      XDestroyImage(xim);
+      xim = 0;
+    } else {
+      vlog.error("XShmCreateImage failed");
+    }
+
+    delete shminfo;
+    shminfo = 0;
+  }
+#endif
+
+  xim = XCreateImage(dpy, vis, depth, ZPixmap,
+                     0, 0, width(), height(), BitmapPad(dpy), 0);
+
+  xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+  if (!xim->data) {
+    vlog.error("malloc failed");
+    exit(1);
+  }
+}
+
+void TXImage::destroyXImage()
+{
+#ifdef HAVE_MITSHM
+  if (shminfo) {
+    vlog.debug("Freeing shared memory XImage");
+    shmdt(shminfo->shmaddr);
+    shmctl(shminfo->shmid, IPC_RMID, 0);
+    delete shminfo;
+    shminfo = 0;
+    imageCleanup.images.remove(this);
+  }
+#endif
+  // XDestroyImage() will free(xim->data) if appropriate
+  if (xim) XDestroyImage(xim);
+  xim = 0;
+}
+
+
+static bool supportedBPP(int bpp) {
+  return (bpp == 8 || bpp == 16 || bpp == 32);
+}
+
+static int depth2bpp(Display* dpy, int depth)
+{
+  int nformats;
+  XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
+
+  int i;
+  for (i = 0; i < nformats; i++)
+    if (format[i].depth == depth) break;
+
+  if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
+    throw rfb::Exception("Error: couldn't find suitable pixmap format");
+
+  int bpp = format[i].bits_per_pixel;
+  XFree(format);
+  return bpp;
+}
+
+void TXImage::getNativePixelFormat(Visual* vis, int depth)
+{
+  cube = 0;
+  nativePF.depth = depth;
+  nativePF.bpp = depth2bpp(dpy, depth);
+  nativePF.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
+  nativePF.trueColour = (vis->c_class == TrueColor);
+
+  vlog.info("Using default colormap and visual, %sdepth %d.",
+            (vis->c_class == TrueColor) ? "TrueColor, " :
+            ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
+            depth);
+
+  if (nativePF.trueColour) {
+
+    nativePF.redShift   = ffs(vis->red_mask)   - 1;
+    nativePF.greenShift = ffs(vis->green_mask) - 1;
+    nativePF.blueShift  = ffs(vis->blue_mask)  - 1;
+    nativePF.redMax   = vis->red_mask   >> nativePF.redShift;
+    nativePF.greenMax = vis->green_mask >> nativePF.greenShift;
+    nativePF.blueMax  = vis->blue_mask  >> nativePF.blueShift;
+
+  } else {
+
+    XColor xc[256];
+    cube = new rfb::ColourCube(6,6,6);
+    int r;
+    for (r = 0; r < cube->nRed; r++) {
+      for (int g = 0; g < cube->nGreen; g++) {
+        for (int b = 0; b < cube->nBlue; b++) {
+          int i = (r * cube->nGreen + g) * cube->nBlue + b;
+          xc[i].red =   r * 65535 / (cube->nRed-1);
+          xc[i].green = g * 65535 / (cube->nGreen-1);
+          xc[i].blue =  b * 65535 / (cube->nBlue-1);
+        }
+      }
+    }
+
+    TXWindow::getColours(dpy, xc, cube->size());
+
+    for (r = 0; r < cube->nRed; r++) {
+      for (int g = 0; g < cube->nGreen; g++) {
+        for (int b = 0; b < cube->nBlue; b++) {
+          int i = (r * cube->nGreen + g) * cube->nBlue + b;
+          cube->set(r, g, b, xc[i].pixel);
+        }
+      }
+    }
+  }
+}
diff --git a/unix/tx/TXImage.h b/unix/tx/TXImage.h
new file mode 100644
index 0000000..055bd22
--- /dev/null
+++ b/unix/tx/TXImage.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXImage.h
+//
+// A TXImage represents a rectangular off-screen image in any RFB pixel format.
+// By default it will use the "native" pixel format for the screen, which will
+// be an 8-bit colourmap unless the X display is TrueColor.  The pixel format
+// can be changed via the setPF() method.  The pixel data is accessible via the
+// data member inherited from FullFramePixelBuffer, or can be set via the
+// fillRect(), imageRect(), copyRect() and maskRect() methods, also inherited
+// from PixelBuffer.  A rectangle of the image can be drawn into an X Window
+// via the put() method.  If using a colourmap, the setColourMapEntries() and
+// updateColourMap() methods must be called to set up the colourmap as
+// appropriate.
+
+
+#ifndef __TXIMAGE_H__
+#define __TXIMAGE_H__
+
+#include <X11/Xlib.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ColourCube.h>
+#ifdef HAVE_MITSHM
+#include <X11/extensions/XShm.h>
+#endif
+
+namespace rfb { class TransImageGetter; }
+
+class TXImage : public rfb::FullFramePixelBuffer, public rfb::ColourMap {
+public:
+  TXImage(Display* dpy, int width, int height, Visual* vis=0, int depth=0);
+  ~TXImage();
+
+  // resize() resizes the image, preserving the image data where possible.
+  void resize(int w, int h);
+
+  // put causes the given rectangle to be drawn onto the given window.
+  void put(Window win, GC gc, const rfb::Rect& r);
+
+  // setColourMapEntries() changes some of the entries in the colourmap.
+  // However these settings won't take effect until updateColourMap() is
+  // called.  This is because recalculating the internal translation table can
+  // be expensive.
+  void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+  void updateColourMap();
+
+#ifdef HAVE_MITSHM
+  bool usingShm() { return shminfo; }
+#else
+  bool usingShm() { return 0; }
+#endif
+
+  // PixelBuffer methods
+  // width(), height(), getPF() etc are inherited from PixelBuffer
+  virtual void setPF(const rfb::PixelFormat& pf);
+  virtual int getStride() const;
+
+private:
+
+  // ColourMap method
+  virtual void lookup(int index, int* r, int* g, int* b);
+
+  void createXImage();
+  void destroyXImage();
+  void getNativePixelFormat(Visual* vis, int depth);
+
+  XImage* xim;
+  Display* dpy;
+  Visual* vis;
+  int depth;
+#ifdef HAVE_MITSHM
+  XShmSegmentInfo* shminfo;
+#endif
+  rfb::TransImageGetter* tig;
+  rfb::Colour colourMap[256];
+  rfb::PixelFormat nativePF;
+  rfb::ColourCube* cube;
+};
+
+#endif
diff --git a/unix/tx/TXLabel.h b/unix/tx/TXLabel.h
new file mode 100644
index 0000000..3d5200d
--- /dev/null
+++ b/unix/tx/TXLabel.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXLabel.h
+//
+// An TXLabel allows you to put up multiline text in a window with various
+// alignments.  The label must be big enough to contain the text - if not then
+// it will be resized appropriately.
+//
+
+#ifndef __TXLABEL_H__
+#define __TXLABEL_H__
+
+#include <stdlib.h>
+#include "TXWindow.h"
+#include <rfb/util.h>
+
+class TXLabel : public TXWindow, public TXEventHandler {
+public:
+  enum HAlign { left, centre, right };
+  enum VAlign { top, middle, bottom };
+
+  TXLabel(Display* dpy_, const char* text_, TXWindow* parent_=0,
+          int w=1, int h=1, HAlign ha=centre, VAlign va=middle)
+    : TXWindow(dpy_, w, h, parent_), lineSpacing(2), lines(0),
+      halign(ha), valign(va)
+  {
+    setEventHandler(this);
+    setText(text_);
+    addEventMask(ExposureMask);
+  }
+
+  // setText() changes the text in the label.
+  void setText(const char* text_) {
+    text.buf = rfb::strDup(text_);
+    lines = 0;
+    int lineStart = 0;
+    int textWidth = 0;
+    int i = -1;
+    do {
+      i++;
+      if (text.buf[i] == '\n' || text.buf[i] == 0) {
+        int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+        if (tw > textWidth) textWidth = tw;
+        lineStart = i+1;
+        lines++;
+      }
+    } while (text.buf[i] != 0);
+    int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+                      * lines);
+    int newWidth = __rfbmax(width(), textWidth + xPad*2);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2);
+    if (width() < newWidth || height() < newHeight) {
+      resize(newWidth, newHeight);
+    }
+    invalidate();
+  }
+
+private:
+  int xOffset(int textWidth) {
+    switch (halign) {
+    case left:  return xPad;
+    case right: return width() - xPad - textWidth;
+    default:    return (width() - textWidth) / 2;
+    }
+  }
+
+  int yOffset(int lineNum) {
+    int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+                      * lines);
+    int lineOffset = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
+                      * lineNum + defaultFS->ascent);
+    switch (valign) {
+    case top:    return yPad + lineOffset;
+    case bottom: return height() - yPad - textHeight + lineOffset;
+    default:     return (height() - textHeight) / 2 + lineOffset;
+    }
+  }
+
+  void paint() {
+    int lineNum = 0;
+    int lineStart = 0;
+    int i = -1;
+    do {
+      i++;
+      if (text.buf[i] == '\n' || text.buf[i] == 0) {
+        int tw = XTextWidth(defaultFS, &text.buf[lineStart], i-lineStart);
+        XDrawString(dpy, win(), defaultGC, xOffset(tw), yOffset(lineNum),
+                    &text.buf[lineStart], i-lineStart);
+        lineStart = i+1;
+        lineNum++;
+      }
+    } while (text.buf[i] != 0);
+  }
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev) {
+    switch (ev->type) {
+    case Expose:
+      paint();
+      break;
+    }
+  }
+
+  int lineSpacing;
+  rfb::CharArray text;
+  int lines;
+  HAlign halign;
+  VAlign valign;
+};
+
+#endif
diff --git a/unix/tx/TXMenu.cxx b/unix/tx/TXMenu.cxx
new file mode 100644
index 0000000..92712f5
--- /dev/null
+++ b/unix/tx/TXMenu.cxx
@@ -0,0 +1,186 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXMenu.cxx
+//
+
+#include "TXMenu.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <rfb/util.h>
+#include <X11/keysym.h>
+
+TXMenu::TXMenu(Display* dpy_, TXMenuCallback* cb_, int w, int h,
+               TXWindow* parent_)
+  : TXWindow(dpy_, w, h, parent_), cb(cb_), nEntries(0),
+    highlight(-1)
+{
+  setEventHandler(this);
+  gc = XCreateGC(dpy, win(), 0, 0);
+  addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+               PointerMotionMask | EnterWindowMask | LeaveWindowMask);
+}
+
+TXMenu::~TXMenu()
+{
+  XFreeGC(dpy, gc);
+  for (int i = 0; i < nEntries; i++)
+    delete [] text[i];
+}
+
+inline int TXMenu::entryHeight(int i)
+{
+  if (text[i])
+    return defaultFS->ascent + defaultFS->descent + bevel*2 + yPad*2;
+  else
+    return yPad*2 + 1;
+}
+
+void TXMenu::addEntry(const char* text_, long id_)
+{
+  assert(nEntries < maxEntries);
+  text[nEntries] = rfb::strDup(text_);
+  checked[nEntries] = false;
+  id[nEntries++] = id_;
+  int tw = 0;
+  if (text_)
+    tw = XTextWidth(defaultFS, text_, strlen(text_));
+  int newWidth = width();
+  if (tw + bevel*2 + xPad*5 + tickSize > width())
+    newWidth = tw + bevel*2 + xPad*5 + tickSize;
+  int newHeight = 0;
+  for (int i = 0; i < nEntries; i++)
+    newHeight += entryHeight(i);
+  resize(newWidth, newHeight);
+}
+
+void TXMenu::check(long id_, bool checked_)
+{
+  for (int i = 0; i < nEntries; i++) {
+    if (id[i] == id_) {
+      checked[i] = checked_;
+      break;
+    }
+  }
+}
+
+void TXMenu::paint()
+{
+  int y = 0;
+  for (int i = 0; i < nEntries; i++) {
+    if (text[i]) {
+      if (i == highlight)
+        drawBevel(gc, 0, y, width(), entryHeight(i), bevel,
+                  defaultBg, darkBg, lightBg);
+      else
+        XClearArea(dpy, win(), 0, y, width(), entryHeight(i), false);
+      if (checked[i])
+        XCopyPlane(dpy, tick, win(), defaultGC, 0, 0, tickSize, tickSize,
+                   bevel + xPad,
+                   y + bevel + yPad + defaultFS->ascent - tickSize, 1);
+
+      XDrawImageString(dpy, win(), defaultGC, bevel + xPad*2 + tickSize,
+                       y + bevel + yPad + defaultFS->ascent,
+                       text[i], strlen(text[i]));
+    } else {
+      XDrawLine(dpy, win(), defaultGC, bevel + xPad, y + entryHeight(i) / 2,
+                width() - bevel - xPad, y + entryHeight(i) / 2);
+    }
+    y += entryHeight(i);
+  }
+}
+
+void TXMenu::handleEvent(TXWindow* w, XEvent* ev)
+{
+  switch (ev->type) {
+  case Expose:
+    paint();
+    break;
+
+  case ButtonRelease:
+    {
+      int y = ev->xmotion.y;
+      int entryY = 0;
+      for (int i = 0; i < nEntries; i++) {
+        if (y >= entryY && y <= entryY + entryHeight(i)) {
+          if (cb && text[i])
+            cb->menuSelect(id[i], this);
+          break;
+        }
+        entryY += entryHeight(i);
+      }
+      highlight = -1;
+      paint();
+      break;
+    }
+
+  case ButtonPress:
+  case MotionNotify:
+    {
+      int y = ev->xmotion.y;
+      int entryY = 0;
+      for (int i = 0; i < nEntries; i++) {
+        if (y >= entryY && y <= entryY + entryHeight(i)) {
+          if (highlight != i) {
+            highlight = i;
+            paint();
+          }
+          break;
+        }
+        entryY += entryHeight(i);
+      }
+      break;
+    }
+
+  case KeyPress:
+    {
+      KeySym ks;
+      char str[256];
+      XLookupString(&ev->xkey, str, 256, &ks, NULL);
+      if (ks == XK_Escape) {
+        highlight = -1;
+        unmap();
+      } else if (ks == XK_Down || ks == XK_Up) {
+        if (nEntries < 1) break;
+        if (highlight < 0)
+          highlight = (ks == XK_Down ? nEntries-1 : 0);
+        int start = highlight;
+        int inc = (ks == XK_Down ? 1 : nEntries-1);
+        do {
+          highlight = (highlight + inc) % nEntries;
+        } while (highlight != start && !text[highlight]);
+        paint();
+      } else if (ks == XK_space || ks == XK_KP_Space ||
+                 ks == XK_Return || ks == XK_KP_Enter) {
+        if (cb && highlight >= 0 && text[highlight])
+          cb->menuSelect(id[highlight], this);
+        highlight = -1;
+        paint();
+      }
+      break;
+    }
+
+  case EnterNotify:
+  case LeaveNotify:
+    highlight = -1;
+    paint();
+    break;
+  }
+}
diff --git a/unix/tx/TXMenu.h b/unix/tx/TXMenu.h
new file mode 100644
index 0000000..d0245ee
--- /dev/null
+++ b/unix/tx/TXMenu.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXMenu.h
+//
+// A TXMenu consists of multiple entries which can be added one at a time.
+// Each entry consists of some text, and has an associated integer identifier.
+// A callback is made when a menu entry is selected.
+//
+
+#ifndef __TXMENU_H__
+#define __TXMENU_H__
+
+#include "TXWindow.h"
+
+// TXMenuCallback's menuSelect() method is called when a particular menu entry
+// is selected.  The id argument identifies the menu entry.
+class TXMenu;
+class TXMenuCallback {
+public:
+  virtual void menuSelect(long id, TXMenu* menu)=0;
+};
+
+class TXMenu : public TXWindow, public TXEventHandler {
+public:
+  TXMenu(Display* dpy_, TXMenuCallback* cb=0, int width=1, int height=1,
+         TXWindow* parent_=0);
+  virtual ~TXMenu();
+
+  // addEntry() adds an entry to the end of the menu with the given text and
+  // identifier.
+  void addEntry(const char* text, long id);
+
+  // check() sets whether the given menu entry should have a tick next to it.
+  void check(long id, bool checked);
+
+private:
+  int entryHeight(int i);
+  virtual void handleEvent(TXWindow* w, XEvent* ev);
+  void paint();
+
+  GC gc;
+  TXMenuCallback* cb;
+  enum { maxEntries = 64 };
+  char* text[maxEntries];
+  long id[maxEntries];
+  bool checked[maxEntries];
+  int nEntries;
+  int highlight;
+};
+
+#endif
diff --git a/unix/tx/TXMsgBox.h b/unix/tx/TXMsgBox.h
new file mode 100644
index 0000000..ff84e6a
--- /dev/null
+++ b/unix/tx/TXMsgBox.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXMsgBox.h
+//
+// A TXMsgBox is a specialised pop-up dialog window, designed to present
+// the user with a small amount of textual information, and potentially to
+// obtain their response.
+// TXMsgBoxes are always modal, and may have an Ok button, Ok+Cancel buttons,
+// or Yes+No buttons.
+// The MsgBox helper function creates a TXMsgBox on the fly, runs it, and
+// returns the result.
+//
+
+#ifndef __TXMSGBOX_H__
+#define __TXMSGBOX_H__
+
+#include "TXDialog.h"
+#include "TXLabel.h"
+#include "TXButton.h"
+
+enum TXMsgBoxFlags {
+  MB_OK = 0,
+  MB_OKCANCEL = 1,
+  MB_YESNO = 4,
+  MB_ICONERROR = 0x10,
+  MB_ICONQUESTION = 0x20,
+  MB_ICONWARNING = 0x30,
+  MB_ICONINFORMATION = 0x40,
+  MB_DEFBUTTON1 = 0,
+  MB_DEFBUTTON2 = 0x100
+};
+
+class TXMsgBox : public TXDialog, public TXButtonCallback {
+public:
+  TXMsgBox(Display* dpy, const char* text, unsigned int flags, const char* title=0)
+    : TXDialog(dpy, 1, 1, "Message", true),
+      textLabel(dpy, "", this),
+    okButton(dpy, "OK", this, this, 60),
+    cancelButton(dpy, "Cancel", this, this, 60)
+  {
+    textLabel.xPad = 8;
+    textLabel.move(0, yPad*4);
+    textLabel.setText(text);
+    resize(textLabel.width(),
+           textLabel.height() + okButton.height() + yPad*12);
+
+    switch (flags & 0x30) {
+    case MB_ICONERROR:
+      toplevel("Error", this); break;
+    case MB_ICONQUESTION:
+      toplevel("Question", this); break;
+    case MB_ICONWARNING:
+      toplevel("Warning", this); break;
+    case MB_ICONINFORMATION:
+      toplevel("Information", this); break;
+    default:
+      if (title)
+	toplevel(title, this);
+      break;
+    };
+
+    switch (flags & 0x7) {
+    default:
+      okButton.move((width() - okButton.width()) / 2,
+		    height() - yPad*4 - okButton.height());
+      cancelButton.unmap();
+      break;
+    case MB_OKCANCEL:
+    case MB_YESNO:
+      
+      okButton.move(((width()/2) - okButton.width()) / 2,
+		    height() - yPad*4 - okButton.height());
+      cancelButton.move(((width()*3/2) - cancelButton.width()) / 2,
+			height() - yPad*4 - cancelButton.height());
+      if ((flags & 0x7) == MB_YESNO) {
+	okButton.setText("Yes");
+	cancelButton.setText("No");
+      }
+      break;
+    };
+
+    setBorderWidth(1);
+  }
+
+  virtual void buttonActivate(TXButton* b) {
+    ok = (b == &okButton);
+    done = true; 
+    unmap();
+  }
+
+  TXLabel textLabel;
+  TXButton okButton;
+  TXButton cancelButton;
+};
+
+#endif
diff --git a/unix/tx/TXScrollbar.cxx b/unix/tx/TXScrollbar.cxx
new file mode 100644
index 0000000..47e124c
--- /dev/null
+++ b/unix/tx/TXScrollbar.cxx
@@ -0,0 +1,119 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXScrollbar.cxx
+//
+
+#include "TXScrollbar.h"
+#include <stdio.h>
+#include <assert.h>
+
+TXScrollbar::TXScrollbar(Display* dpy_, int width, int height, bool vert,
+                         TXScrollbarCallback* cb_, TXWindow* parent_)
+  : TXWindow(dpy_, width, height, parent_), cb(cb_), vertical(vert),
+    clickedInThumb(false)
+{
+  setEventHandler(this);
+  gc = XCreateGC(dpy, win(), 0, 0);
+  addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask |
+               ButtonMotionMask);
+  setBg(scrollbarBg);
+  limit[0] = len[0] = limit[1] = len[1] = 1;
+  start[0] = start[1] = 0;
+}
+
+TXScrollbar::~TXScrollbar()
+{
+  XFreeGC(dpy, gc);
+}
+
+void TXScrollbar::set(int limit_, int start_, int len_, bool vert)
+{
+  assert(limit_ > 0 && len_ >= 0 && len_ <= limit_);
+
+  if (start_ < 0) start_ = 0;
+  if (start_ > limit_ - len_) start_ = limit_ - len_;
+
+  if (limit[vert] != limit_ || start[vert] != start_ || len[vert] != len_) {
+    limit[vert] = limit_;
+    start[vert] = start_;
+    len[vert] = len_;
+    paint();
+  }
+}
+
+void TXScrollbar::paint()
+{
+  int x = scaleToBarX(start[0]);
+  int y = scaleToBarY(start[1]);
+  int w = scaleToBarX(len[0]);
+  int h = scaleToBarY(len[1]);
+  if (y > 0) XClearArea(dpy, win(), 0, 0, 0, y, false);
+  if (x > 0) XClearArea(dpy, win(), 0, y, x, y+h, false);
+  XClearArea(dpy, win(), x+w, y, 0, y+h, false);
+  XClearArea(dpy, win(), 0, y+h, 0, 0, false);
+  drawBevel(gc, x, y, w, h, bevel, defaultBg, lightBg, darkBg);
+}
+
+void TXScrollbar::handleEvent(TXWindow* w, XEvent* ev)
+{
+  switch (ev->type) {
+  case Expose:
+    paint();
+    break;
+
+  case ButtonPress:
+    {
+      xDown = ev->xbutton.x;
+      yDown = ev->xbutton.y;
+      xStart = start[0];
+      yStart = start[1];
+      bool clickedInThumbX = false;
+      if (xDown < scaleToBarX(start[0])) {
+        set(limit[0], start[0] - len[0], len[0], false);
+      } else if (xDown >= scaleToBarX(start[0]+len[0])) {
+        set(limit[0], start[0] + len[0], len[0], false);
+      } else {
+        clickedInThumbX = true;
+      }
+      bool clickedInThumbY = false;
+      if (yDown < scaleToBarY(start[1])) {
+        set(limit[1], start[1] - len[1], len[1], true);
+      } else if (yDown >= scaleToBarY(start[1]+len[1])) {
+        set(limit[1], start[1] + len[1], len[1], true);
+      } else {
+        clickedInThumbY = true;
+      }
+      clickedInThumb = clickedInThumbX && clickedInThumbY;
+      if (cb) cb->scrollbarPos(start[0], start[1], this);
+    }
+    break;
+
+  case ButtonRelease:
+  case MotionNotify:
+    while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
+    if (clickedInThumb) {
+      int dx = ev->xmotion.x - xDown;
+      int dy = ev->xmotion.y - yDown;
+      set(limit[0], xStart + barToScaleX(dx), len[0], false);
+      set(limit[1], yStart + barToScaleY(dy), len[1], true);
+      if (cb) cb->scrollbarPos(start[0], start[1], this);
+    }
+    break;
+  }
+}
diff --git a/unix/tx/TXScrollbar.h b/unix/tx/TXScrollbar.h
new file mode 100644
index 0000000..87fec3d
--- /dev/null
+++ b/unix/tx/TXScrollbar.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXScrollbar.h
+//
+// A TXScrollbar represents a range of values starting at start, of length len,
+// between zero and limit.  The vertical argument to the constructor says
+// whether the scrollbar is horizontal or vertical.
+//
+// In fact it can represent a range in each dimension but usually one of the
+// dimensions is fixed, according to the vertical flag (for a vertical
+// scrollbar, the horizontal dimension is fixed, and vice-versa).
+//
+// The TXScrollbarCallback argument is an object which will be notified when
+// the user has attempted to move the scrollbar.  The x and y arguments to the
+// scrollbarPos() method give the start values in the respective dimensions.
+// They are guaranteed to be between 0 and limit-len.
+//
+
+#ifndef __TXSCROLLBAR_H__
+#define __TXSCROLLBAR_H__
+
+#include "TXWindow.h"
+
+class TXScrollbarCallback;
+
+class TXScrollbar : public TXWindow, public TXEventHandler {
+public:
+  TXScrollbar(Display* dpy_, int width=1, int height=1, bool vertical=false,
+              TXScrollbarCallback* cb=0, TXWindow* parent_=0);
+  virtual ~TXScrollbar();
+
+  // set() sets the limit, start and length of the range represented by the
+  // scrollbar.  The values of limit and len passed in must be valid
+  // (i.e. limit > 0 and 0 <= len <= limit).  Values of start are clipped to
+  // the range 0 to limit-len.
+  void set(int limit, int start, int len) { set(limit, start, len, vertical); }
+
+  // set() with an extra argument vert can be used to represent a range in both
+  // dimensions simultaneously.
+  void set(int limit, int start, int len, bool vert);
+
+  virtual void handleEvent(TXWindow* w, XEvent* ev);
+
+private:
+  int scaleToBarX(int x) { return (x * width() + limit[0]/2) / limit[0]; }
+  int scaleToBarY(int y) { return (y * height() + limit[1]/2) / limit[1]; }
+  int barToScaleX(int x) { return (x * limit[0] + width()/2) / width(); }
+  int barToScaleY(int y) { return (y * limit[1] + height()/2) / height(); }
+  void paint();
+
+  GC gc;
+  TXScrollbarCallback* cb;
+  int limit[2];
+  int start[2];
+  int len[2];
+  int xDown, yDown;
+  int xStart, yStart;
+  bool vertical;
+  bool clickedInThumb;
+};
+
+class TXScrollbarCallback {
+public:
+  virtual void scrollbarPos(int x, int y, TXScrollbar* sb)=0;
+};
+#endif
diff --git a/unix/tx/TXViewport.cxx b/unix/tx/TXViewport.cxx
new file mode 100644
index 0000000..abe5173
--- /dev/null
+++ b/unix/tx/TXViewport.cxx
@@ -0,0 +1,155 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXViewport.cxx
+//
+
+#include "TXViewport.h"
+#include <stdio.h>
+
+TXViewport::TXViewport(Display* dpy_, int w, int h, TXWindow* parent_)
+  : TXWindow(dpy_, w, h, parent_), child(0), hScrollbar(0),
+    vScrollbar(0), scrollbarSize(15), xOff(0), yOff(0), bumpScrollTimer(this),
+    bumpScroll(false), needScrollbars(false), bumpScrollX(0), bumpScrollY(0)
+{
+  clipper = new TXWindow(dpy, width()-scrollbarSize, height()-scrollbarSize,
+                         this);
+  clipper->setBg(black);
+  hScrollbar = new TXScrollbar(dpy, width()-scrollbarSize, scrollbarSize,
+                               false, this, this);
+  vScrollbar = new TXScrollbar(dpy, scrollbarSize, height()-scrollbarSize,
+                               true, this, this);
+}
+
+TXViewport::~TXViewport()
+{
+  delete clipper;
+  delete hScrollbar;
+  delete vScrollbar;
+}
+
+void TXViewport::setChild(TXWindow* child_)
+{
+  child = child_;
+  XReparentWindow(dpy, child->win(), clipper->win(), 0, 0);
+  xOff = yOff = 0;
+  child->map();
+  resizeNotify();
+}
+
+bool TXViewport::setOffset(int x, int y)
+{
+  if (clipper->width() >= child->width()) {
+    x = (clipper->width() - child->width()) / 2;
+  } else {
+    if (x > 0) x = 0;
+    if (x + child->width() < clipper->width())
+      x = clipper->width() - child->width();
+  }
+
+  if (clipper->height() >= child->height()) {
+    y = (clipper->height() - child->height()) / 2;
+  } else {
+    if (y > 0) y = 0;
+    if (y + child->height() < clipper->height())
+      y = clipper->height() - child->height();
+  }
+
+  if (x != xOff || y != yOff) {
+    xOff = x;
+    yOff = y;
+    child->move(xOff, yOff);
+    return true;
+  }
+
+  return false;
+}
+
+void TXViewport::setBumpScroll(bool b)
+{
+  bumpScroll = b;
+  resizeNotify();
+}
+
+// Note: bumpScrollEvent() only works if the viewport is positioned at 0,0 and
+// is the same width and height as the screen.
+bool TXViewport::bumpScrollEvent(XMotionEvent* ev)
+{
+  if (!bumpScroll) return false;
+  int bumpScrollPixels = 20;
+  bumpScrollX = bumpScrollY = 0;
+
+  if (ev->x_root == width()-1)  bumpScrollX = -bumpScrollPixels;
+  else if (ev->x_root == 0)     bumpScrollX = bumpScrollPixels;
+  if (ev->y_root == height()-1) bumpScrollY = -bumpScrollPixels;
+  else if (ev->y_root == 0)     bumpScrollY = bumpScrollPixels;
+
+  if (bumpScrollX || bumpScrollY) {
+    if (bumpScrollTimer.isStarted()) return true;
+    if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY)) {
+      bumpScrollTimer.start(25);
+      return true;
+    }
+  }
+
+  bumpScrollTimer.stop();
+  return false;
+}
+
+bool TXViewport::handleTimeout(rfb::Timer* timer) {
+  return setOffset(xOff + bumpScrollX, yOff + bumpScrollY);
+}
+
+void TXViewport::resizeNotify()
+{
+  needScrollbars = (!bumpScroll &&
+                    (width() < child->width() || height() < child->height()) &&
+                    (width() > scrollbarSize && height() > scrollbarSize));
+  if (needScrollbars) {
+    clipper->resize(width()-scrollbarSize, height()-scrollbarSize);
+    hScrollbar->map();
+    vScrollbar->map();
+  } else {
+    clipper->resize(width(), height());
+    hScrollbar->unmap();
+    vScrollbar->unmap();
+  }
+
+  setOffset(xOff, yOff);
+
+  if (needScrollbars) {
+    hScrollbar->move(0, height()-scrollbarSize);
+    hScrollbar->resize(width()-scrollbarSize, scrollbarSize);
+    hScrollbar->set(child->width(), -xOff, width()-scrollbarSize);
+    vScrollbar->move(width()-scrollbarSize, 0);
+    vScrollbar->resize(scrollbarSize, height()-scrollbarSize);
+    vScrollbar->set(child->height(), -yOff, height()-scrollbarSize);
+  }
+}
+
+void TXViewport::scrollbarPos(int x, int y, TXScrollbar* sb)
+{
+  if (sb == hScrollbar) {
+    x = -x;
+    y = yOff;
+  } else {
+    x = xOff;
+    y = -y;
+  }
+  setOffset(x, y);
+}
diff --git a/unix/tx/TXViewport.h b/unix/tx/TXViewport.h
new file mode 100644
index 0000000..0c9857d
--- /dev/null
+++ b/unix/tx/TXViewport.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXViewport.h
+//
+// A TXViewport allows a large window to be viewed by adding scrollbars to the
+// right and bottom if necessary.  It also has a bump-scroll mode where there
+// are no scrollbars, and scrolling is achieved by bumping up against the edge
+// of the screen instead.  Note that this only works when the viewport fills
+// the entire screen.  If the child window is smaller than the viewport, it is
+// always positioned centrally in the viewport.
+
+#ifndef __TXVIEWPORT_H__
+#define __TXVIEWPORT_H__
+
+#include <rfb/Timer.h>
+#include "TXWindow.h"
+#include "TXScrollbar.h"
+
+class TXViewport : public TXWindow, public TXScrollbarCallback,
+                   public rfb::Timer::Callback {
+public:
+  TXViewport(Display* dpy_, int width, int height, TXWindow* parent_=0);
+  virtual ~TXViewport();
+
+  // setChild() sets the child window which is to be viewed in the viewport.
+  void setChild(TXWindow* child_);
+
+  // setOffset() sets the position of the child in the viewport.  Note that the
+  // offsets are negative.  For example when the offset is (-100,-30), position
+  // (100,30) in the child window is at the top-left of the viewport.  The
+  // offsets given are clipped to keep the child window filling the viewport
+  // (except where the child window is smaller than the viewport, in which case
+  // it is always positioned centrally in the viewport).  It returns true if
+  // the child was repositioned.
+  bool setOffset(int x, int y);
+
+  // setBumpScroll() puts the viewport in bump-scroll mode.
+  void setBumpScroll(bool b);
+
+  // bumpScrollEvent() can be called with a MotionNotify event which may
+  // potentially be against the edge of the screen.  It returns true if the
+  // event was used for bump-scrolling, false if it should be processed
+  // normally.
+  bool bumpScrollEvent(XMotionEvent* ev);
+
+private:
+  virtual void resizeNotify();
+  virtual void scrollbarPos(int x, int y, TXScrollbar* sb);
+  virtual bool handleTimeout(rfb::Timer* timer);
+  TXWindow* clipper;
+  TXWindow* child;
+  TXScrollbar* hScrollbar;
+  TXScrollbar* vScrollbar;
+  const int scrollbarSize;
+  int xOff, yOff;
+  rfb::Timer bumpScrollTimer;
+  bool bumpScroll;
+  bool needScrollbars;
+  int bumpScrollX, bumpScrollY;
+};
+#endif
diff --git a/unix/tx/TXWindow.cxx b/unix/tx/TXWindow.cxx
new file mode 100644
index 0000000..6d211be
--- /dev/null
+++ b/unix/tx/TXWindow.cxx
@@ -0,0 +1,486 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXWindow.cxx
+//
+
+#include <X11/Xatom.h>
+#include "TXWindow.h"
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb/util.h>
+
+std::list<TXWindow*> windows;
+
+Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+Atom xaCLIPBOARD;
+unsigned long TXWindow::black, TXWindow::white;
+unsigned long TXWindow::defaultFg, TXWindow::defaultBg;
+unsigned long TXWindow::lightBg, TXWindow::darkBg;
+unsigned long TXWindow::disabledFg, TXWindow::disabledBg;
+unsigned long TXWindow::enabledBg;
+unsigned long TXWindow::scrollbarBg;
+Colormap TXWindow::cmap = 0;
+GC TXWindow::defaultGC = 0;
+Font TXWindow::defaultFont = 0;
+XFontStruct* TXWindow::defaultFS = 0;
+Time TXWindow::cutBufferTime = 0;
+Pixmap TXWindow::dot = 0, TXWindow::tick = 0;
+const int TXWindow::dotSize = 4, TXWindow::tickSize = 8;
+char* TXWindow::defaultWindowClass;
+
+void TXWindow::init(Display* dpy, const char* defaultWindowClass_)
+{
+  cmap = DefaultColormap(dpy,DefaultScreen(dpy));
+  wmProtocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+  wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+  wmTakeFocus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+  xaTIMESTAMP = XInternAtom(dpy, "TIMESTAMP", False);
+  xaTARGETS = XInternAtom(dpy, "TARGETS", False);
+  xaSELECTION_TIME = XInternAtom(dpy, "SELECTION_TIME", False);
+  xaSELECTION_STRING = XInternAtom(dpy, "SELECTION_STRING", False);
+  xaCLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False);
+  XColor cols[6];
+  cols[0].red = cols[0].green = cols[0].blue = 0x0000;
+  cols[1].red = cols[1].green = cols[1].blue = 0xbbbb;
+  cols[2].red = cols[2].green = cols[2].blue = 0xeeee;
+  cols[3].red = cols[3].green = cols[3].blue = 0x5555;
+  cols[4].red = cols[4].green = cols[4].blue = 0x8888;
+  cols[5].red = cols[5].green = cols[5].blue = 0xffff;
+  getColours(dpy, cols, 6);
+  black = defaultFg = cols[0].pixel;
+  defaultBg = disabledBg = cols[1].pixel;
+  lightBg = cols[2].pixel;
+  darkBg = disabledFg = cols[3].pixel;
+  scrollbarBg = cols[4].pixel;
+  white = enabledBg = cols[5].pixel;
+  defaultGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);
+  defaultFS
+    = XLoadQueryFont(dpy, "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
+  if (!defaultFS) {
+    defaultFS = XLoadQueryFont(dpy, "fixed");
+    if (!defaultFS) {
+      fprintf(stderr,"Failed to load any font\n");
+      exit(1);
+    }
+  }
+  defaultFont = defaultFS->fid;
+  XSetForeground(dpy, defaultGC, defaultFg);
+  XSetBackground(dpy, defaultGC, defaultBg);
+  XSetFont(dpy, defaultGC, defaultFont);
+  XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask);
+
+  static char dotBits[] = { 0x06, 0x0f, 0x0f, 0x06};
+  dot = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), dotBits,
+                              dotSize, dotSize);
+  static char tickBits[] = { 0x80, 0xc0, 0xe2, 0x76, 0x3e, 0x1c, 0x08, 0x00};
+  tick = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), tickBits,
+                               tickSize, tickSize);
+  defaultWindowClass = rfb::strDup(defaultWindowClass_);
+}
+
+void TXWindow::handleXEvents(Display* dpy)
+{
+  while (XPending(dpy)) {
+    XEvent ev;
+    XNextEvent(dpy, &ev);
+    if (ev.type == MappingNotify) {
+      XRefreshKeyboardMapping(&ev.xmapping);
+    } else if (ev.type == PropertyNotify &&
+               ev.xproperty.window == DefaultRootWindow(dpy) &&
+               ev.xproperty.atom == XA_CUT_BUFFER0) {
+      cutBufferTime = ev.xproperty.time;
+    } else {
+      std::list<TXWindow*>::iterator i;
+      for (i = windows.begin(); i != windows.end(); i++) {
+        if ((*i)->win() == ev.xany.window)
+          (*i)->handleXEvent(&ev);
+      }
+    }
+  }
+}
+
+void TXWindow::getColours(Display* dpy, XColor* cols, int nCols)
+{
+  bool* got = new bool[nCols];
+  bool failed = false;
+  int i;
+  for (i = 0; i < nCols; i++) {
+    if (XAllocColor(dpy, cmap, &cols[i])) {
+      got[i] = true;
+    } else {
+      got[i] = false;
+      failed = true;
+    }
+  }
+
+  if (!failed) {
+    delete [] got;
+    return;
+  }
+
+  // AllocColor has failed.  This is because the colormap is full.  So the
+  // only thing we can do is use the "shared" pixels in the colormap.  The
+  // code below is designed to work even when the colormap isn't full so is
+  // more complex than it needs to be in this case.  However it would be
+  // useful one day to be able to restrict the number of colours allocated by
+  // an application so I'm leaving it in here.
+
+  // For each pixel in the colormap, try to allocate exactly its RGB values.
+  // If this returns a different pixel then it must be a private or
+  // unallocated pixel, so we can't use it.  If it returns us the same pixel
+  // again, it's almost certainly a shared colour, so we can use it (actually
+  // it is possible that it was an unallocated pixel which we've now
+  // allocated - by going through the pixels in reverse order we make this
+  // unlikely except for the lowest unallocated pixel - this works because of
+  // the way the X server allocates new pixels).
+
+  int cmapSize = DisplayCells(dpy,DefaultScreen(dpy));
+
+  XColor* cm = new XColor[cmapSize];
+  bool* shared = new bool[cmapSize];
+  bool* usedAsNearest = new bool[cmapSize];
+
+  for (i = 0; i < cmapSize; i++) {
+    cm[i].pixel = i;
+    shared[i] = usedAsNearest[i] = false;
+  }
+
+  XQueryColors(dpy, cmap, cm, cmapSize);
+
+  for (i = cmapSize-1; i >= 0; i--) {
+    if (XAllocColor(dpy, cmap, &cm[i])) {
+      if (cm[i].pixel == (unsigned long)i) {
+        shared[i] = true;
+      } else {
+        XFreeColors(dpy, cmap, &cm[i].pixel, 1, 0);
+      }
+    }
+  }
+
+  for (int j = 0; j < nCols; j++) {
+    unsigned long minDistance = ULONG_MAX;
+    unsigned long nearestPixel = 0;
+    if (!got[j]) {
+      for (i = 0; i < cmapSize; i++) {
+        if (shared[i]) {
+          unsigned long rd = (cm[i].red - cols[j].red)/2;
+          unsigned long gd = (cm[i].green - cols[j].green)/2;
+          unsigned long bd = (cm[i].blue - cols[j].blue)/2;
+          unsigned long distance = (rd*rd + gd*gd + bd*bd);
+
+          if (distance < minDistance) {
+            minDistance = distance;
+            nearestPixel = i;
+          }
+        }
+      }
+
+      cols[j].pixel = nearestPixel;
+      usedAsNearest[nearestPixel] = true;
+    }
+  }
+
+  for (i = 0; i < cmapSize; i++) {
+    if (shared[i] && !usedAsNearest[i]) {
+      unsigned long p = i;
+      XFreeColors(dpy, cmap, &p, 1, 0);
+    }
+  }
+}
+
+Window TXWindow::windowWithName(Display* dpy, Window top, const char* name)
+{
+  char* windowName;
+  if (XFetchName(dpy, top, &windowName)) {
+    if (strcmp(windowName, name) == 0) {
+      XFree(windowName);
+      return top;
+    }
+    XFree(windowName);
+  }
+
+  Window* children;
+  Window dummy;
+  unsigned int nchildren;
+  if (!XQueryTree(dpy, top, &dummy, &dummy, &children,&nchildren) || !children)
+    return 0;
+
+  for (int i = 0; i < (int)nchildren; i++) {
+    Window w = windowWithName(dpy, children[i], name);
+    if (w) {
+      XFree((char*)children);
+      return w;
+    }
+  }
+  XFree((char*)children);
+  return 0;
+}
+
+
+TXWindow::TXWindow(Display* dpy_, int w, int h, TXWindow* parent_,
+                   int borderWidth)
+  : dpy(dpy_), xPad(3), yPad(3), bevel(2), parent(parent_), width_(w),
+    height_(h), eventHandler(0), dwc(0), eventMask(0), toplevel_(false)
+{
+  sizeHints.flags = 0;
+  XSetWindowAttributes attr;
+  attr.background_pixel = defaultBg;
+  attr.border_pixel = 0;
+  Window par = parent ? parent->win() : DefaultRootWindow(dpy);
+  win_ = XCreateWindow(dpy, par, 0, 0, width_, height_, borderWidth,
+                      CopyFromParent, CopyFromParent, CopyFromParent,
+                      CWBackPixel | CWBorderPixel, &attr);
+  if (parent) map();
+
+  windows.push_back(this);
+}
+
+TXWindow::~TXWindow()
+{
+  windows.remove(this);
+  XDestroyWindow(dpy, win());
+}
+
+void TXWindow::toplevel(const char* name, TXDeleteWindowCallback* dwc_,
+                        int argc, char** argv, const char* windowClass,
+                        bool iconic)
+{
+  toplevel_ = true;
+  XWMHints wmHints;
+  wmHints.flags = InputHint|StateHint;
+  wmHints.input = True;
+  wmHints.initial_state = iconic ? IconicState : NormalState;
+  XClassHint classHint;
+  if (!windowClass) windowClass = defaultWindowClass;
+  classHint.res_name = (char*)name;
+  classHint.res_class = (char*)windowClass;
+  XSetWMProperties(dpy, win(), 0, 0, argv, argc,
+                   &sizeHints, &wmHints, &classHint);
+  XStoreName(dpy, win(), name);
+  XSetIconName(dpy, win(), name);
+  Atom protocols[10];
+  int nProtocols = 0;
+  protocols[nProtocols++] = wmTakeFocus;
+  dwc = dwc_;
+  if (dwc)
+    protocols[nProtocols++] = wmDeleteWindow;
+  XSetWMProtocols(dpy, win(), protocols, nProtocols);
+  addEventMask(StructureNotifyMask);
+}
+
+void TXWindow::setMaxSize(int w, int h)
+{
+  sizeHints.flags |= PMaxSize;
+  sizeHints.max_width = w;
+  sizeHints.max_height = h;
+  XSetWMNormalHints(dpy, win(), &sizeHints);
+}
+
+void TXWindow::setUSPosition(int x, int y)
+{
+  sizeHints.flags |= USPosition;
+  sizeHints.x = x;
+  sizeHints.y = y;
+  XSetWMNormalHints(dpy, win(), &sizeHints);
+  move(x, y);
+}
+
+void TXWindow::setGeometry(const char* geom, int x, int y, int w, int h)
+{
+  char defGeom[256];
+  sprintf(defGeom,"%dx%d+%d+%d",w,h,x,y);
+  XWMGeometry(dpy, DefaultScreen(dpy), strEmptyToNull((char*)geom), defGeom,
+              0, &sizeHints, &x, &y, &w, &h, &sizeHints.win_gravity);
+  sizeHints.flags |= PWinGravity;
+  setUSPosition(x, y);
+  resize(w, h);
+}
+
+TXEventHandler* TXWindow::setEventHandler(TXEventHandler* h)
+{
+  TXEventHandler* old = eventHandler;
+  eventHandler = h;
+  return old;
+}
+
+void TXWindow::addEventMask(long mask)
+{
+  eventMask |= mask;
+  XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::removeEventMask(long mask)
+{
+  eventMask &= ~mask;
+  XSelectInput(dpy, win(), eventMask);
+}
+
+void TXWindow::unmap()
+{
+  XUnmapWindow(dpy, win());
+  if (toplevel_) {
+    XUnmapEvent ue;
+    ue.type = UnmapNotify;
+    ue.display = dpy;
+    ue.event = DefaultRootWindow(dpy);
+    ue.window = win();
+    ue.from_configure = False;
+    XSendEvent(dpy, DefaultRootWindow(dpy), False,
+               (SubstructureRedirectMask|SubstructureNotifyMask),
+               (XEvent*)&ue);
+  }
+}
+
+void TXWindow::resize(int w, int h)
+{
+  //if (w == width_ && h == height_) return;
+  XResizeWindow(dpy, win(), w, h);
+  width_ = w;
+  height_ = h;
+  resizeNotify();
+}
+
+void TXWindow::setBorderWidth(int bw)
+{
+  XWindowChanges c;
+  c.border_width = bw;
+  XConfigureWindow(dpy, win(), CWBorderWidth, &c);
+}
+
+void TXWindow::ownSelection(Atom selection, Time time)
+{
+  XSetSelectionOwner(dpy, selection, win(), time);
+  if (XGetSelectionOwner(dpy, selection) == win()) {
+    selectionOwner_[selection] = true;
+    selectionOwnTime[selection] = time;
+  }
+}
+
+void TXWindow::handleXEvent(XEvent* ev)
+{
+  switch (ev->type) {
+
+  case ClientMessage:
+    if (ev->xclient.message_type == wmProtocols) {
+      if ((Atom)ev->xclient.data.l[0] == wmDeleteWindow) {
+        if (dwc) dwc->deleteWindow(this);
+      } else if ((Atom)ev->xclient.data.l[0] == wmTakeFocus) {
+        takeFocus(ev->xclient.data.l[1]);
+      }
+    }
+    break;
+
+  case ConfigureNotify:
+    if (ev->xconfigure.width != width_ || ev->xconfigure.height != height_) {
+      width_ = ev->xconfigure.width;
+      height_ = ev->xconfigure.height;
+      resizeNotify();
+    }
+    break;
+
+  case SelectionNotify:
+    if (ev->xselection.property != None) {
+      Atom type;
+      int format;
+      unsigned long nitems, after;
+      unsigned char *data;
+      XGetWindowProperty(dpy, win(), ev->xselection.property, 0, 16384, True,
+                         AnyPropertyType, &type, &format,
+                         &nitems, &after, &data);
+      if (type != None) {
+        selectionNotify(&ev->xselection, type, format, nitems, data);
+        XFree(data);
+        break;
+      }
+    }
+    selectionNotify(&ev->xselection, 0, 0, 0, 0);
+    break;
+
+  case SelectionRequest:
+    {
+      XSelectionEvent se;
+      se.type = SelectionNotify;
+      se.display = ev->xselectionrequest.display;
+      se.requestor = ev->xselectionrequest.requestor;
+      se.selection = ev->xselectionrequest.selection;
+      se.time = ev->xselectionrequest.time;
+      se.target = ev->xselectionrequest.target;
+      if (ev->xselectionrequest.property == None)
+        ev->xselectionrequest.property = ev->xselectionrequest.target;
+      if (!selectionOwner_[se.selection]) {
+        se.property = None;
+      } else {
+        se.property = ev->xselectionrequest.property;
+        if (se.target == xaTARGETS) {
+          Atom targets[2];
+          targets[0] = xaTIMESTAMP;
+          targets[1] = XA_STRING;
+          XChangeProperty(dpy, se.requestor, se.property, XA_ATOM, 32,
+                          PropModeReplace, (unsigned char*)targets, 2);
+        } else if (se.target == xaTIMESTAMP) {
+          rdr::U32 t = selectionOwnTime[se.selection];
+          XChangeProperty(dpy, se.requestor, se.property, XA_INTEGER, 32,
+                          PropModeReplace, (unsigned char*)&t, 1);
+        } else if (se.target == XA_STRING) {
+          if (!selectionRequest(se.requestor, se.selection, se.property))
+            se.property = None;
+        }
+      }
+      XSendEvent(dpy, se.requestor, False, 0, (XEvent*)&se);
+      break;
+    }
+
+  case SelectionClear:
+    selectionOwner_[ev->xselectionclear.selection] = false;
+    break;
+  }
+
+  if (eventHandler) eventHandler->handleEvent(this, ev);
+}
+
+void TXWindow::drawBevel(GC gc, int x, int y, int w, int h, int b,
+                         unsigned long middle, unsigned long tl,
+                         unsigned long br, bool round)
+{
+  if (round) {
+    XGCValues gcv;
+    gcv.line_width = b;
+    XChangeGC(dpy, gc, GCLineWidth, &gcv);
+    XSetForeground(dpy, gc, middle);
+    XFillArc(dpy, win(), gc,  x, y, w-b/2, h-b/2, 0, 360*64);
+    XSetForeground(dpy, gc, tl);
+    XDrawArc(dpy, win(), gc,  x, y, w-b/2, h-b/2, 45*64, 180*64);
+    XSetForeground(dpy, gc, br);
+    XDrawArc(dpy, win(), gc,  x, y, w-b/2, h-b/2, 225*64, 180*64);
+  } else {
+    XSetForeground(dpy, gc, middle);
+    if (w-2*b > 0 && h-2*b > 0)
+      XFillRectangle(dpy, win(), gc, x+b, y+b, w-2*b, h-2*b);
+    XSetForeground(dpy, gc, tl);
+    XFillRectangle(dpy, win(), gc, x, y, w, b);
+    XFillRectangle(dpy, win(), gc, x, y, b, h);
+    XSetForeground(dpy, gc, br);
+    for (int i = 0; i < b; i++) {
+      if (w-i > 0) XFillRectangle(dpy, win(), gc, x+i, y+h-1-i, w-i, 1); 
+      if (h-1-i > 0) XFillRectangle(dpy, win(), gc, x+w-1-i, y+i+1, 1, h-1-i);
+    }
+  }
+}
diff --git a/unix/tx/TXWindow.h b/unix/tx/TXWindow.h
new file mode 100644
index 0000000..5ada181
--- /dev/null
+++ b/unix/tx/TXWindow.h
@@ -0,0 +1,211 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TXWindow.h
+//
+// A TXWindow is the base class for all tx windows (widgets).  In addition it
+// contains a number of static methods and members which are used throughout
+// tx.
+//
+// Before calling any other tx methods, TXWindow::init() must be called with
+// the X display to use.
+
+#ifndef __TXWINDOW_H__
+#define __TXWINDOW_H__
+
+#include <rdr/types.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <map>
+
+
+// TXDeleteWindowCallback's deleteWindow() method is called when a top-level
+// window is "deleted" (closed) by the user using the window manager.
+class TXWindow;
+class TXDeleteWindowCallback {
+public:
+  virtual void deleteWindow(TXWindow* w) = 0;
+};
+
+// TXEventHandler is an interface implemented by classes wanting to handle X
+// events on a window.  Most derived classes of window are their own event
+// handlers.
+class TXEventHandler {
+public:
+  virtual void handleEvent(TXWindow* w, XEvent* ev) = 0;
+};
+
+class TXWindow {
+public:
+
+  // Constructor - creates a window of the given size, with the default
+  // background (currently grey).  It is mapped by default if it has a parent.
+  // If no parent is specified its parent is the root window and it will remain
+  // unmapped.
+  TXWindow(Display* dpy_, int width=1, int height=1, TXWindow* parent_=0,
+           int borderWidth=0);
+  virtual ~TXWindow();
+
+  // toplevel() declares that this is a top-level window.  Various
+  // window-manager-related properties are set on the window.  The given
+  // TXDeleteWindowCallback is notified when the window is "deleted" (cloesd)
+  // by the user.
+  void toplevel(const char* name, TXDeleteWindowCallback* dwc=0,
+                int argc=0, char** argv=0, const char* windowClass=0,
+                bool iconic=false);
+
+  // setMaxSize() tells the window manager the maximum size to allow a
+  // top-level window.  It has no effect on a non-top-level window.
+  void setMaxSize(int w, int h);
+
+  // setUSPosition() tells the window manager the position which the "user" has
+  // asked for a top-level window.  Most window managers ignore requests by a
+  // program for position, so you have to tell it that the "user" asked for the
+  // position.  This has no effect on a non-top-level window.
+  void setUSPosition(int x, int y);
+
+  void setGeometry(const char* geom, int x, int y, int w, int h);
+
+  // setTransientFor() tells the window manager that this window is "owned" by
+  // the given window.  The window manager can use this information as it sees
+  // fit.
+  void setTransientFor(Window w) { XSetTransientForHint(dpy, win(), w); }
+
+  // setEventHandler() sets the TXEventHandler to handle X events for this
+  // window.  It returns the previous event handler, so that handlers can chain
+  // themselves.
+  TXEventHandler* setEventHandler(TXEventHandler* h);
+
+  // Accessor methods
+  Window win() { return win_; }
+  int width() { return width_; }
+  int height() { return height_; }
+
+  // selectionOwner() returns true if this window owns the given selection.
+  bool selectionOwner(Atom selection) { return selectionOwner_[selection]; }
+
+  // Wrappers around common Xlib calls
+  void addEventMask(long mask);
+  void removeEventMask(long mask);
+  void map()                   { XMapWindow(dpy, win()); }
+  void unmap();
+  void setBg(unsigned long bg) { XSetWindowBackground(dpy, win(), bg); }
+  void move(int x, int y)      { XMoveWindow(dpy, win(), x, y); }
+  void resize(int w, int h);
+  void raise()                 { XRaiseWindow(dpy, win()); }
+  void setBorderWidth(int bw);
+  void invalidate(int x=0, int y=0, int w=0, int h=0) { XClearArea(dpy, win(), x, y, w, h, True); }
+
+  // ownSelection requests that the window owns the given selection from the
+  // given time (the time should be taken from an X event).
+  void ownSelection(Atom selection, Time time);
+
+
+  // drawBevel draws a rectangular or circular bevel filling the given
+  // rectangle, using the given colours for the middle, the top/left and the
+  // bottom/right.
+  void drawBevel(GC gc, int x, int y, int w, int h, int b,
+                 unsigned long middle, unsigned long tl, unsigned long br,
+                 bool round=false);
+
+  // Methods to be overridden in a derived class
+
+  // resizeNotify() is called whenever the window's dimensions may have
+  // changed.
+  virtual void resizeNotify() {}
+
+  // takeFocus() is called when the window has received keyboard focus from the
+  // window manager.
+  virtual void takeFocus(Time time) {}
+
+  // selectionNotify() is called when the selection owner has replied to a
+  // request for information about a selection from the selection owner.
+  virtual void selectionNotify(XSelectionEvent* ev, Atom type, int format,
+                               int nitems, void* data) {}
+
+  // selectionRequest() is called when this window is the selection owner and
+  // another X client has requested the selection.  It should set the given
+  // property on the given window to the value of the given selection,
+  // returning true if successful, false otherwise.
+  virtual bool selectionRequest(Window requestor,
+                                Atom selection, Atom property) { return false;}
+
+  // Static methods
+
+  // init() must be called before any other tx methods.
+  static void init(Display* dpy, const char* defaultWindowClass);
+
+  // getColours() sets the pixel values in the cols array to the best available
+  // for the given rgb values, even in the case of a full colormap.
+  static void getColours(Display* dpy, XColor* cols, int nCols);
+
+  // handleXEvents() should be called whenever there are events to handle on
+  // the connection to the X display.  It process all available events, then
+  // returns when there are no more events to process.
+  static void handleXEvents(Display* dpy);
+
+  // windowWithName() locates a window with a given name on a display.
+  static Window windowWithName(Display* dpy, Window top, const char* name);
+
+  // strEmptyToNull() returns the string it's given but turns an empty string
+  // into null, which can be useful for passing rfb parameters to Xlib calls.
+  static char* strEmptyToNull(char* s) { return s && s[0] ? s : 0; }
+
+  // The following are default values for various things.
+  static unsigned long black, white;
+  static unsigned long defaultFg, defaultBg, lightBg, darkBg;
+  static unsigned long disabledFg, disabledBg, enabledBg;
+  static unsigned long scrollbarBg;
+  static GC defaultGC;
+  static Colormap cmap;
+  static Font defaultFont;
+  static XFontStruct* defaultFS;
+  static Time cutBufferTime;
+  static Pixmap dot, tick;
+  static const int dotSize, tickSize;
+  static char* defaultWindowClass;
+
+  Display* const dpy;
+
+  int xPad, yPad, bevel;
+
+private:
+
+  // handleXEvent() is called from handleXEvents() when an event for this
+  // window arrives.  It does general event processing before calling on to the
+  // event handler.
+  void handleXEvent(XEvent* ev);
+
+  TXWindow* parent;
+  Window win_;
+  int width_, height_;
+  TXEventHandler* eventHandler;
+  TXDeleteWindowCallback* dwc;
+  long eventMask;
+  XSizeHints sizeHints;
+  std::map<Atom,Time> selectionOwnTime;
+  std::map<Atom,bool> selectionOwner_;
+  bool toplevel_;
+};
+
+extern Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
+extern Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
+extern Atom xaCLIPBOARD;
+
+#endif