uncommitted - blackbox

Ready changes

Summary

Import uploads missing from VCS:

Diff

diff --git a/.pc/.quilt_patches b/.pc/.quilt_patches
new file mode 100644
index 0000000..6857a8d
--- /dev/null
+++ b/.pc/.quilt_patches
@@ -0,0 +1 @@
+debian/patches
diff --git a/.pc/.quilt_series b/.pc/.quilt_series
new file mode 100644
index 0000000..c206706
--- /dev/null
+++ b/.pc/.quilt_series
@@ -0,0 +1 @@
+series
diff --git a/.pc/.version b/.pc/.version
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/.pc/.version
@@ -0,0 +1 @@
+2
diff --git a/.pc/010_pkgconfig-requires-private.patch/lib/libbt.pc.in b/.pc/010_pkgconfig-requires-private.patch/lib/libbt.pc.in
new file mode 100644
index 0000000..a2a8869
--- /dev/null
+++ b/.pc/010_pkgconfig-requires-private.patch/lib/libbt.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+Name: Blackbox Toolbox
+Description: Utility class library for writing small applications
+Requires: @XFT_PKGCONFIG@
+Version: @VERSION@
+Libs: -L${libdir} -lbt @LDFLAGS@ @ICONV@ @LOCALE@
+Cflags: -I${includedir}/bt
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/Image.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/Image.cc
new file mode 100644
index 0000000..ebce0b1
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/Image.cc
@@ -0,0 +1,1934 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Image.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "Image.hh"
+#include "Display.hh"
+#include "Pen.hh"
+#include "Texture.hh"
+
+#include <algorithm>
+#include <vector>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#ifdef    MITSHM
+#  include <sys/types.h>
+#  include <sys/ipc.h>
+#  include <sys/shm.h>
+#  include <unistd.h>
+#  include <X11/extensions/XShm.h>
+#endif // MITSHM
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// #define COLORTABLE_DEBUG
+// #define MITSHM_DEBUG
+
+
+static unsigned int right_align(unsigned int v)
+{
+  while (!(v & 0x1))
+    v >>= 1;
+  return v;
+}
+
+static int lowest_bit(unsigned int v)
+{
+  int i;
+  unsigned int b = 1u;
+  for (i = 0; ((v & b) == 0u) && i < 32;  ++i)
+    b <<= 1u;
+  return i == 32 ? -1 : i;
+}
+
+
+unsigned int bt::Image::global_maximumColors = 0u; // automatic
+bt::DitherMode bt::Image::global_ditherMode = bt::OrderedDither;
+
+
+namespace bt {
+
+  class XColorTable {
+  public:
+    XColorTable(const Display &dpy, unsigned int screen,
+                unsigned int maxColors);
+    ~XColorTable(void);
+
+    inline bt::DitherMode ditherMode(void) const
+    {
+      return ((n_red < 256u || n_green < 256u || n_blue < 256u)
+              ? bt::Image::ditherMode()
+              : bt::NoDither);
+    }
+
+    void map(unsigned int &red,
+             unsigned int &green,
+             unsigned int &blue);
+    unsigned long pixel(unsigned int red,
+                        unsigned int green,
+                        unsigned int blue);
+
+  private:
+    const Display &_dpy;
+    unsigned int _screen;
+    int visual_class;
+    unsigned int n_red, n_green, n_blue;
+    int red_shift, green_shift, blue_shift;
+
+    std::vector<unsigned long> colors;
+  };
+
+
+  typedef std::vector<unsigned char> Buffer;
+  static Buffer buffer;
+
+
+  typedef std::vector<XColorTable*> XColorTableList;
+  static XColorTableList colorTableList;
+
+
+  void destroyColorTables(void) {
+    XColorTableList::iterator it = colorTableList.begin(),
+                             end = colorTableList.end();
+    for (; it != end; ++it) {
+      if (*it)
+        delete *it;
+      *it = 0;
+    }
+    colorTableList.clear();
+    buffer.clear();
+  }
+
+
+#ifdef MITSHM
+  static XShmSegmentInfo shm_info;
+  static bool use_shm = false;
+  static bool shm_attached = false;
+  static int shm_id = -1;
+  static char *shm_addr = reinterpret_cast<char *>(-1);
+
+
+  static int handleShmError(::Display *, XErrorEvent *) {
+    use_shm = false;
+    return 0;
+  }
+
+
+  void startupShm(const Display &display) {
+    // query MIT-SHM extension
+    if (!XShmQueryExtension(display.XDisplay()))
+      return;
+    use_shm = true;
+  }
+
+
+  void destroyShmImage(const Display &display, XImage *image) {
+    // tell the X server to detach
+    if (shm_attached) {
+      XShmDetach(display.XDisplay(), &shm_info);
+
+      // wait for the server to render the image and detach the memory
+      // segment
+      XSync(display.XDisplay(), False);
+
+      shm_attached = false;
+    }
+
+    // detach shared memory segment
+    if (shm_addr != reinterpret_cast<char *>(-1))
+      shmdt(shm_addr);
+    shm_addr = reinterpret_cast<char *>(-1);
+
+    // destroy shared memory id
+    if (shm_id != -1)
+      shmctl(shm_id, IPC_RMID, 0);
+    shm_id = -1;
+
+    // destroy XImage
+    image->data = 0;
+    XDestroyImage(image);
+  }
+
+
+  XImage *createShmImage(const Display &display, const ScreenInfo &screeninfo,
+                         unsigned int width, unsigned int height) {
+    if (!use_shm)
+      return 0;
+
+    // use MIT-SHM extension
+    XImage *image = XShmCreateImage(display.XDisplay(), screeninfo.visual(),
+                                    screeninfo.depth(), ZPixmap, 0,
+                                    &shm_info, width, height);
+    if (!image)
+      return 0;
+
+    // get shared memory id
+    unsigned int usage = image->bytes_per_line * image->height;
+    shm_id = shmget(IPC_PRIVATE, usage, IPC_CREAT | 0644);
+    if (shm_id == -1) {
+#ifdef MITSHM_DEBUG
+      perror("bt::createShmImage: shmget");
+#endif // MITSHM_DEBUG
+
+      use_shm = false;
+      XDestroyImage(image);
+      return 0;
+    }
+
+    // attached shared memory segment
+    shm_addr = static_cast<char *>(shmat(shm_id, 0, 0));
+    if (shm_addr == reinterpret_cast<char *>(-1)) {
+#ifdef MITSHM_DEBUG
+      perror("bt::createShmImage: shmat");
+#endif // MITSHM_DEBUG
+
+      use_shm = false;
+      destroyShmImage(display, image);
+      return 0;
+    }
+
+    // tell the X server to attach
+    shm_info.shmid = shm_id;
+    shm_info.shmaddr = shm_addr;
+    shm_info.readOnly = True;
+
+    static bool test_server_attach = true;
+    if (test_server_attach) {
+      // never checked if the X server can do shared memory...
+      XErrorHandler old_handler = XSetErrorHandler(handleShmError);
+      XShmAttach(display.XDisplay(), &shm_info);
+      XSync(display.XDisplay(), False);
+      XSetErrorHandler(old_handler);
+
+      if (!use_shm) {
+        // the X server failed to attach the shm segment
+
+#ifdef MITSHM_DEBUG
+        fprintf(stderr, "bt::createShmImage: X server failed to attach\n");
+#endif // MITSHM_DEBUG
+
+        destroyShmImage(display, image);
+        return 0;
+      }
+
+      test_server_attach = false;
+    } else {
+      // we know the X server can attach to the memory segment
+      XShmAttach(display.XDisplay(), &shm_info);
+    }
+
+    shm_attached = true;
+    image->data = shm_addr;
+    return image;
+  }
+#endif // MITSHM
+
+} // namespace bt
+
+
+bt::XColorTable::XColorTable(const Display &dpy, unsigned int screen,
+                             unsigned int maxColors)
+  : _dpy(dpy), _screen(screen),
+    n_red(0u), n_green(0u), n_blue(0u),
+    red_shift(0u), green_shift(0u), blue_shift(0u)
+{
+  const ScreenInfo &screeninfo = _dpy.screenInfo(_screen);
+  const Visual * const visual = screeninfo.visual();
+  const Colormap colormap = screeninfo.colormap();
+
+  visual_class = visual->c_class;
+
+  bool query_colormap = false;
+
+  switch (visual_class) {
+  case StaticGray:
+  case GrayScale:
+    {
+      if (visual_class == GrayScale) {
+        if (maxColors == 0u) {
+          // inspired by libXmu...
+          if (visual->map_entries > 65000)
+            maxColors = 4096;
+          else if (visual->map_entries > 4000)
+            maxColors = 512;
+          else if (visual->map_entries > 250)
+            maxColors = 12;
+          else
+            maxColors = 4;
+        }
+
+        maxColors =
+          std::min(static_cast<unsigned int>(visual->map_entries), maxColors);
+        n_red = n_green = n_blue = maxColors;
+      } else {
+        n_red = n_green = n_blue = visual->map_entries;
+        query_colormap = true;
+      }
+
+      colors.resize(n_green);
+
+      const unsigned int g_max = n_green - 1;
+      const unsigned int g_round = g_max / 2;
+
+      for (unsigned int g = 0; g < n_green; ++g) {
+        colors[g] = ~0ul;
+
+        if (visual_class & 1) {
+          const int gray = (g * 0xffff + g_round) / g_max;
+
+          XColor xcolor;
+          xcolor.red   = gray;
+          xcolor.green = gray;
+          xcolor.blue  = gray;
+          xcolor.pixel = 0ul;
+
+          if (XAllocColor(_dpy.XDisplay(), colormap, &xcolor))
+            colors[g] = xcolor.pixel;
+          else
+            query_colormap = true;
+        }
+      }
+      break;
+    }
+
+  case StaticColor:
+  case PseudoColor:
+    {
+      if (visual_class == PseudoColor) {
+        if (maxColors == 0u) {
+          // inspired by libXmu...
+          if (visual->map_entries > 65000)
+            maxColors = 32 * 32 * 16;
+          else if (visual->map_entries > 4000)
+            maxColors = 16 * 16 * 8;
+          else if (visual->map_entries > 250)
+            maxColors = visual->map_entries - 125;
+          else
+            maxColors = visual->map_entries;
+        }
+
+        maxColors =
+          std::min(static_cast<unsigned int>(visual->map_entries), maxColors);
+
+        // calculate 2:2:1 proportions
+        n_red   = 2u;
+        n_green = 2u;
+        n_blue  = 2u;
+        for (;;) {
+          if ((n_blue * 2u) < n_red
+              && (n_blue + 1u) * n_red * n_green <= maxColors)
+            ++n_blue;
+          else if (n_red < n_green
+                   && n_blue * (n_red + 1u) * n_green <= maxColors)
+            ++n_red;
+          else if (n_blue * n_red * (n_green + 1u) <= maxColors)
+            ++n_green;
+          else
+            break;
+        }
+      } else {
+        n_red   = right_align(visual->red_mask)   + 1;
+        n_green = right_align(visual->green_mask) + 1;
+        n_blue  = right_align(visual->blue_mask)  + 1;
+        query_colormap = true;
+      }
+
+      colors.resize(n_red * n_green * n_blue);
+
+      const int r_max = n_red - 1;
+      const int r_round = r_max / 2;
+      const int g_max = n_green - 1;
+      const int g_round = g_max / 2;
+      const int b_max = n_blue - 1;
+      const int b_round = b_max / 2;
+
+      // create color cube
+      for (unsigned int x = 0, r = 0; r < n_red; ++r) {
+        for (unsigned int g = 0; g < n_green; ++g) {
+          for (unsigned int b = 0; b < n_blue; ++b, ++x) {
+            colors[x] = ~0ul;
+
+            if (visual_class & 1) {
+              XColor xcolor;
+              xcolor.red   = (r * 0xffff + r_round) / r_max;
+              xcolor.green = (g * 0xffff + g_round) / g_max;
+              xcolor.blue  = (b * 0xffff + b_round) / b_max;
+              xcolor.pixel = 0ul;
+
+              if (XAllocColor(_dpy.XDisplay(), colormap, &xcolor))
+                colors[x] = xcolor.pixel;
+              else
+                query_colormap = true;
+            }
+          }
+        }
+      }
+      break;
+    }
+
+  case TrueColor:
+    n_red   = right_align(visual->red_mask)   + 1;
+    n_green = right_align(visual->green_mask) + 1;
+    n_blue  = right_align(visual->blue_mask)  + 1;
+
+    red_shift = lowest_bit(visual->red_mask);
+    green_shift = lowest_bit(visual->green_mask);
+    blue_shift = lowest_bit(visual->blue_mask);
+    break;
+
+  case DirectColor:
+    // from libXmu...
+    n_red = n_green = n_blue = (visual->map_entries / 2) - 1;
+    break;
+  } // switch
+
+#ifdef COLORTABLE_DEBUG
+  switch (visual_class) {
+  case StaticGray:
+  case GrayScale:
+    for (int x = 0; x < colors.size(); ++x) {
+      const int gray = (x * 0xffff + (n_green - 1) / 2) / (n_green - 1);
+      fprintf(stderr, "%s %3u: gray %04x\n",
+              colors[x] == ~0ul ? "req  " : "alloc", x, gray);
+    }
+    break;
+  case StaticColor:
+  case PseudoColor:
+    for (int x = 0; x < colors.size(); ++x) {
+      int r = (x / (n_green * n_blue)) % n_red;
+      int g = (x / n_blue) % n_green;
+      int b = x % n_blue;
+
+      r = (r * 0xffff + (n_red - 1) / 2) / (n_red - 1);
+      g = (g * 0xffff + (n_green - 1) / 2) / (n_green - 1);
+      b = (b * 0xffff + (n_blue - 1) / 2) / (n_blue - 1);
+
+      fprintf(stderr, "%s %3u: %04x/%04x/%04x\n",
+              colors[x] == ~0ul ? "req  " : "alloc", x, r, g, b);
+    }
+    break;
+  default:
+    break;
+  }
+#endif // COLORTABLE_DEBUG
+
+  if (colors.empty() || !query_colormap)
+    return;
+
+  // query existing colormap
+  const int depth = screeninfo.depth();
+  int q_colors = (((1u << depth) > 256u) ? 256u : (1u << depth));
+  XColor queried[256];
+  for (int x = 0; x < q_colors; ++x)
+    queried[x].pixel = x;
+  XQueryColors(_dpy.XDisplay(), colormap, queried, q_colors);
+
+#ifdef COLORTABLE_DEBUG
+  for (int x = 0; x < q_colors; ++x) {
+    if (queried[x].red == 0
+        && queried[x].green == 0
+        && queried[x].blue == 0
+        && queried[x].pixel != BlackPixel(_dpy.XDisplay(), _screen)) {
+      q_colors = x;
+      break;
+    }
+
+    fprintf(stderr, "query %3u: %04x/%04x/%04x\n",
+            x, queried[x].red, queried[x].green, queried[x].blue);
+  }
+#endif // COLORTABLE_DEBUG
+
+  // for missing colors, find the closest color in the existing colormap
+  for (unsigned int x = 0; x < colors.size(); ++x) {
+    if (colors[x] != ~0ul)
+      continue;
+
+    int red = 0, green = 0, blue = 0, gray = 0;
+
+    switch (visual_class) {
+    case StaticGray:
+    case GrayScale:
+      red = green = blue = gray =
+            (x * 0xffff - (n_green - 1) / 2) / (n_green - 1);
+      break;
+    case StaticColor:
+    case PseudoColor:
+      red = (x / (n_green * n_blue)) % n_red;
+      green = (x / n_blue) % n_green;
+      blue = x % n_blue;
+
+      red = (red * 0xffff + (n_red - 1) / 2) / (n_red - 1);
+      green = (green * 0xffff + (n_green - 1) / 2) / (n_green - 1);
+      blue = (blue * 0xffff + (n_blue - 1) / 2) / (n_blue - 1);
+
+      gray = ((red * 30 + green * 59 + blue * 11) / 100);
+      break;
+    default:
+      assert(false);
+    }
+
+    int mindist = INT_MAX, best = -1;
+    for (int y = 0; y < q_colors; ++y) {
+      const int r = (red - queried[y].red) >> 8;
+      const int g = (green - queried[y].green) >> 8;
+      const int b = (blue - queried[y].blue) >> 8;
+      const int dist = (r * r) + (g * g) + (b * b);
+
+      if (dist < mindist) {
+        mindist = dist;
+        best = y;
+      }
+    }
+    assert(best >= 0 && best < q_colors);
+
+#ifdef COLORTABLE_DEBUG
+    fprintf(stderr, "close %3u: %04x/%04x/%04x\n",
+            x, queried[best].red, queried[best].green, queried[best].blue);
+#endif // COLORTABLE_DEBUG
+
+    if (visual_class & 1) {
+      XColor xcolor = queried[best];
+
+      if (XAllocColor(_dpy.XDisplay(), colormap, &xcolor)) {
+        colors[x] = xcolor.pixel;
+      } else {
+        colors[x] = gray < SHRT_MAX
+                    ? BlackPixel(_dpy.XDisplay(), _screen)
+                    : WhitePixel(_dpy.XDisplay(), _screen);
+      }
+    } else {
+      colors[x] = best;
+    }
+  }
+}
+
+
+bt::XColorTable::~XColorTable(void) {
+  if (!colors.empty()) {
+    XFreeColors(_dpy.XDisplay(), _dpy.screenInfo(_screen).colormap(),
+                &colors[0], colors.size(), 0);
+    colors.clear();
+  }
+}
+
+
+void bt::XColorTable::map(unsigned int &red,
+                          unsigned int &green,
+                          unsigned int &blue) {
+  red   = (red   * n_red)   >> 8;
+  green = (green * n_green) >> 8;
+  blue  = (blue  * n_blue)  >> 8;
+}
+
+
+unsigned long bt::XColorTable::pixel(unsigned int red,
+                                     unsigned int green,
+                                     unsigned int blue) {
+  switch (visual_class) {
+  case StaticGray:
+  case GrayScale:
+    return colors[(red * 30 + green * 59 + blue * 11) / 100];
+
+  case StaticColor:
+  case PseudoColor:
+    return colors[(red * n_green * n_blue) + (green * n_blue) + blue];
+
+  case TrueColor:
+  case DirectColor:
+    return ((red << red_shift)
+            | (green << green_shift)
+            | (blue << blue_shift));
+  }
+
+  // not reached
+  return 0; // shut up compiler warning
+}
+
+
+bt::Image::Image(unsigned int w, unsigned int h)
+  : data(0), width(w), height(h)
+{
+  assert(width > 0);
+  assert(height > 0);
+}
+
+
+bt::Image::~Image(void) {
+  delete [] data;
+  data = 0;
+}
+
+
+Pixmap bt::Image::render(const Display &display, unsigned int screen,
+                         const bt::Texture &texture) {
+  if (texture.texture() & bt::Texture::Parent_Relative)
+    return ParentRelative;
+  if (texture.texture() & bt::Texture::Solid)
+    return None;
+  if (!(texture.texture() & bt::Texture::Gradient))
+    return None;
+
+  const Color from = texture.color1(), to = texture.color2();
+  const bool interlaced = texture.texture() & bt::Texture::Interlaced;
+
+  data = new RGB[width * height];
+
+  if (texture.texture() & bt::Texture::Diagonal)
+    dgradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::Elliptic)
+    egradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::Horizontal)
+    hgradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::Pyramid)
+    pgradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::Rectangle)
+    rgradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::Vertical)
+    vgradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::CrossDiagonal)
+    cdgradient(from, to, interlaced);
+  else if (texture.texture() & bt::Texture::PipeCross)
+    pcgradient(from, to, interlaced);
+
+  if (texture.texture() & bt::Texture::Raised)
+    raisedBevel(texture.borderWidth());
+  else if (texture.texture() & bt::Texture::Sunken)
+    sunkenBevel(texture.borderWidth());
+
+  Pixmap pixmap = renderPixmap(display, screen);
+
+  unsigned int bw = 0;
+  if (texture.texture() & bt::Texture::Border) {
+    Pen penborder(screen, texture.borderColor());
+    bw = texture.borderWidth();
+
+    for (unsigned int i = 0; i < bw; ++i) {
+      XDrawRectangle(penborder.XDisplay(), pixmap, penborder.gc(),
+                     i, i, width - (i * 2) - 1, height - (i * 2) - 1);
+    }
+  }
+
+  return pixmap;
+}
+
+
+/*
+ * Ordered dither table
+ */
+static const unsigned int dither16[16][16] = {
+  {     0, 49152, 12288, 61440,  3072, 52224, 15360, 64512,
+      768, 49920, 13056, 62208,  3840, 52992, 16128, 65280 },
+  { 32768, 16384, 45056, 28672, 35840, 19456, 48128, 31744,
+    33536, 17152, 45824, 29440, 36608, 20224, 48896, 32512 },
+  {  8192, 57344,  4096, 53248, 11264, 60416,  7168, 56320,
+     8960, 58112,  4864, 54016, 12032, 61184,  7936, 57088 },
+  { 40960, 24576, 36864, 20480, 44032, 27648, 39936, 23552,
+    41728, 25344, 37632, 21248, 44800, 28416, 40704, 24320 },
+  {  2048, 51200, 14336, 63488,  1024, 50176, 13312, 62464,
+     2816, 51968, 15104, 64256,  1792, 50944, 14080, 63232 },
+  { 34816, 18432, 47104, 30720, 33792, 17408, 46080, 29696,
+    35584, 19200, 47872, 31488, 34560, 18176, 46848, 30464 },
+  { 10240, 59392,  6144, 55296,  9216, 58368,  5120, 54272,
+    11008, 60160,  6912, 56064,  9984, 59136,  5888, 55040 },
+  { 43008, 26624, 38912, 22528, 41984, 25600, 37888, 21504,
+    43776, 27392, 39680, 23296, 42752, 26368, 38656, 22272 },
+  {   512, 49664, 12800, 61952,  3584, 52736, 15872, 65024,
+      256, 49408, 12544, 61696,  3328, 52480, 15616, 64768 },
+  { 33280, 16896, 45568, 29184, 36352, 19968, 48640, 32256,
+    33024, 16640, 45312, 28928, 36096, 19712, 48384, 32000 },
+  {  8704, 57856,  4608, 53760, 11776, 60928,  7680, 56832,
+     8448, 57600,  4352, 53504, 11520, 60672,  7424, 56576 },
+  { 41472, 25088, 37376, 20992, 44544, 28160, 40448, 24064,
+    41216, 24832, 37120, 20736, 44288, 27904, 40192, 23808 },
+  {  2560, 51712, 14848, 64000,  1536, 50688, 13824, 62976,
+     2304, 51456, 14592, 63744,  1280, 50432, 13568, 62720 },
+  { 35328, 18944, 47616, 31232, 34304, 17920, 46592, 30208,
+    35072, 18688, 47360, 30976, 34048, 17664, 46336, 29952 },
+  { 10752, 59904,  6656, 55808,  9728, 58880,  5632, 54784,
+    10496, 59648,  6400, 55552,  9472, 58624,  5376, 54528 },
+  { 43520, 27136, 39424, 23040, 42496, 26112, 38400, 22016,
+    43264, 26880, 39168, 22784, 42240, 25856, 38144, 21760 }
+};
+
+
+/*
+ * Helper function for TrueColorDither and renderXImage
+ *
+ * This handles the proper setting of the image data based on the image depth
+ * and the machine's byte ordering
+ */
+static void assignPixelData(unsigned int bit_depth, unsigned char **data,
+                            unsigned long pixel) {
+  unsigned char *pixel_data = *data;
+  switch (bit_depth) {
+  case  8: //  8bpp
+    pixel_data[0] = pixel;
+    ++pixel_data;
+    break;
+
+  case 16: // 16bpp LSB
+    pixel_data[0] = pixel;
+    pixel_data[1] = pixel >> 8;
+    pixel_data += 2;
+    break;
+
+  case 17: // 16bpp MSB
+    pixel_data[0] = pixel >> 8;
+    pixel_data[1] = pixel;
+    pixel_data += 2;
+    break;
+
+  case 24: // 24bpp LSB
+    pixel_data[0] = pixel;
+    pixel_data[1] = pixel >> 8;
+    pixel_data[2] = pixel >> 16;
+    pixel_data += 3;
+    break;
+
+  case 25: // 24bpp MSB
+    pixel_data[0] = pixel >> 16;
+    pixel_data[1] = pixel >> 8;
+    pixel_data[2] = pixel;
+    pixel_data += 3;
+    break;
+
+  case 32: // 32bpp LSB
+    pixel_data[0] = pixel;
+    pixel_data[1] = pixel >> 8;
+    pixel_data[2] = pixel >> 16;
+    pixel_data[3] = pixel >> 24;
+    pixel_data += 4;
+    break;
+
+  case 33: // 32bpp MSB
+    pixel_data[0] = pixel >> 24;
+    pixel_data[1] = pixel >> 16;
+    pixel_data[2] = pixel >> 8;
+    pixel_data[3] = pixel;
+    pixel_data += 4;
+    break;
+  }
+  *data = pixel_data; // assign back so we don't lose our place
+}
+
+
+// algorithm: ordered dithering... many many thanks to rasterman
+// (raster@rasterman.com) for telling me about this... portions of this
+// code is based off of his code in Imlib
+void bt::Image::OrderedDither(XColorTable *colortable,
+                              unsigned int bit_depth,
+                              unsigned int bytes_per_line,
+                              unsigned char *pixel_data) {
+  unsigned int x, y, dithx, dithy, r, g, b, error, offset;
+  unsigned char *ppixel_data = pixel_data;
+
+  unsigned int maxr = 255, maxg = 255, maxb = 255;
+  colortable->map(maxr, maxg, maxb);
+
+  for (y = 0, offset = 0; y < height; ++y) {
+    dithy = y & 15;
+
+    for (x = 0; x < width; ++x, ++offset) {
+      dithx = x & 15;
+
+      error = dither16[dithy][dithx];
+
+      r = (((256 * maxr + maxr + 1) * data[offset].red   + error) / 65536);
+      g = (((256 * maxg + maxg + 1) * data[offset].green + error) / 65536);
+      b = (((256 * maxb + maxb + 1) * data[offset].blue  + error) / 65536);
+
+      assignPixelData(bit_depth, &pixel_data, colortable->pixel(r, g, b));
+    }
+
+    pixel_data = (ppixel_data += bytes_per_line);
+  }
+}
+
+
+void bt::Image::FloydSteinbergDither(XColorTable *colortable,
+                                     unsigned int bit_depth,
+                                     unsigned int bytes_per_line,
+                                     unsigned char *pixel_data) {
+  int * const error = new int[width * 6];
+  int * const r_line1 = error + (width * 0);
+  int * const g_line1 = error + (width * 1);
+  int * const b_line1 = error + (width * 2);
+  int * const r_line2 = error + (width * 3);
+  int * const g_line2 = error + (width * 4);
+  int * const b_line2 = error + (width * 5);
+
+  int rer, ger, ber;
+  unsigned int x, y, r, g, b, offset;
+  unsigned char *ppixel_data = pixel_data;
+  RGB *pixels = new RGB[width];
+
+  unsigned int maxr = 255, maxg = 255, maxb = 255;
+  colortable->map(maxr, maxg, maxb);
+  maxr = 255u / maxr;
+  maxg = 255u / maxg;
+  maxb = 255u / maxb;
+
+  for (y = 0, offset = 0; y < height; ++y) {
+    const bool reverse = bool(y & 1);
+
+    int * const rl1 = (reverse) ? r_line2 : r_line1;
+    int * const gl1 = (reverse) ? g_line2 : g_line1;
+    int * const bl1 = (reverse) ? b_line2 : b_line1;
+    int * const rl2 = (reverse) ? r_line1 : r_line2;
+    int * const gl2 = (reverse) ? g_line1 : g_line2;
+    int * const bl2 = (reverse) ? b_line1 : b_line2;
+
+    if (y == 0) {
+      for (x = 0; x < width; ++x) {
+        rl1[x] = static_cast<int>(data[x].red);
+        gl1[x] = static_cast<int>(data[x].green);
+        bl1[x] = static_cast<int>(data[x].blue);
+      }
+    }
+    if (y+1 < height) {
+      for (x = 0; x < width; ++x) {
+        rl2[x] = static_cast<int>(data[offset + width + x].red);
+        gl2[x] = static_cast<int>(data[offset + width + x].green);
+        bl2[x] = static_cast<int>(data[offset + width + x].blue);
+      }
+    }
+
+    // bi-directional dither
+    if (reverse) {
+      for (x = 0; x < width; ++x) {
+        r = static_cast<unsigned int>(std::max(std::min(rl1[x], 255), 0));
+        g = static_cast<unsigned int>(std::max(std::min(gl1[x], 255), 0));
+        b = static_cast<unsigned int>(std::max(std::min(bl1[x], 255), 0));
+
+        colortable->map(r, g, b);
+
+        pixels[x].red   = r;
+        pixels[x].green = g;
+        pixels[x].blue  = b;
+
+        rer = rl1[x] - static_cast<int>(r * maxr);
+        ger = gl1[x] - static_cast<int>(g * maxg);
+        ber = bl1[x] - static_cast<int>(b * maxb);
+
+        if (x+1 < width) {
+          rl1[x+1] += rer * 7 / 16;
+          gl1[x+1] += ger * 7 / 16;
+          bl1[x+1] += ber * 7 / 16;
+          rl2[x+1] += rer * 1 / 16;
+          gl2[x+1] += ger * 1 / 16;
+          bl2[x+1] += ber * 1 / 16;
+        }
+        rl2[x] += rer * 5 / 16;
+        gl2[x] += ger * 5 / 16;
+        bl2[x] += ber * 5 / 16;
+        if (x > 0) {
+          rl2[x-1] += rer * 3 / 16;
+          gl2[x-1] += ger * 3 / 16;
+          bl2[x-1] += ber * 3 / 16;
+        }
+      }
+    } else {
+      for (x = width; x-- > 0; ) {
+        r = static_cast<unsigned int>(std::max(std::min(rl1[x], 255), 0));
+        g = static_cast<unsigned int>(std::max(std::min(gl1[x], 255), 0));
+        b = static_cast<unsigned int>(std::max(std::min(bl1[x], 255), 0));
+
+        colortable->map(r, g, b);
+
+        pixels[x].red   = r;
+        pixels[x].green = g;
+        pixels[x].blue  = b;
+
+        rer = rl1[x] - static_cast<int>(r * maxr);
+        ger = gl1[x] - static_cast<int>(g * maxg);
+        ber = bl1[x] - static_cast<int>(b * maxb);
+
+        if (x > 0) {
+          rl1[x-1] += rer * 7 / 16;
+          gl1[x-1] += ger * 7 / 16;
+          bl1[x-1] += ber * 7 / 16;
+          rl2[x-1] += rer * 1 / 16;
+          gl2[x-1] += ger * 1 / 16;
+          bl2[x-1] += ber * 1 / 16;
+        }
+        rl2[x] += rer * 5 / 16;
+        gl2[x] += ger * 5 / 16;
+        bl2[x] += ber * 5 / 16;
+        if (x+1 < width) {
+          rl2[x+1] += rer * 3 / 16;
+          gl2[x+1] += ger * 3 / 16;
+          bl2[x+1] += ber * 3 / 16;
+        }
+      }
+    }
+    for (x = 0; x < width; ++x) {
+      r = pixels[x].red;
+      g = pixels[x].green;
+      b = pixels[x].blue;
+      assignPixelData(bit_depth, &pixel_data, colortable->pixel(r, g, b));
+    }
+
+    offset += width;
+    pixel_data = (ppixel_data += bytes_per_line);
+  }
+
+  delete [] error;
+  delete [] pixels;
+}
+
+
+Pixmap bt::Image::renderPixmap(const Display &display, unsigned int screen) {
+  // get the colortable for the screen. if necessary, we will create one.
+  if (colorTableList.empty())
+    colorTableList.resize(display.screenCount(), 0);
+
+  if (!colorTableList[screen])
+    colorTableList[screen] = new XColorTable(display, screen, maximumColors());
+
+  XColorTable *colortable = colorTableList[screen];
+  const ScreenInfo &screeninfo = display.screenInfo(screen);
+  XImage *image = 0;
+  bool shm_ok = false;
+
+#ifdef MITSHM
+  // try to use MIT-SHM extension
+  if (use_shm) {
+    image = createShmImage(display, screeninfo, width, height);
+    shm_ok = (image != 0);
+  }
+#endif // MITSHM
+
+  if (!shm_ok) {
+    // regular XImage
+    image = XCreateImage(display.XDisplay(), screeninfo.visual(),
+                         screeninfo.depth(), ZPixmap,
+                         0, 0, width, height, 32, 0);
+    if (!image)
+      return None;
+
+    buffer.reserve(image->bytes_per_line * (height + 1));
+    image->data = reinterpret_cast<char *>(&buffer[0]);
+  }
+
+  unsigned char *d = reinterpret_cast<unsigned char *>(image->data);
+  unsigned int o = image->bits_per_pixel
+                   + ((image->byte_order == MSBFirst) ? 1 : 0);
+
+  DitherMode dmode =
+    (width > 1 && height > 1) ? colortable->ditherMode() : NoDither;
+
+  // render to XImage
+  switch (dmode) {
+  case bt::FloydSteinbergDither:
+    FloydSteinbergDither(colortable, o, image->bytes_per_line, d);
+    break;
+
+  case bt::OrderedDither:
+    OrderedDither(colortable, o, image->bytes_per_line, d);
+    break;
+
+  case bt::NoDither: {
+    unsigned int x, y, offset, r, g, b;
+    unsigned char *pixel_data = d, *ppixel_data = d;
+
+    for (y = 0, offset = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++offset) {
+        r = data[offset].red;
+        g = data[offset].green;
+        b = data[offset].blue;
+
+        colortable->map(r, g, b);
+        assignPixelData(o, &pixel_data, colortable->pixel(r, g, b));
+      }
+
+      pixel_data = (ppixel_data += image->bytes_per_line);
+    }
+    break;
+  }
+  } // switch dmode
+
+  Pixmap pixmap = XCreatePixmap(display.XDisplay(), screeninfo.rootWindow(),
+                                width, height, screeninfo.depth());
+  if (pixmap == None) {
+    image->data = 0;
+    XDestroyImage(image);
+
+    return None;
+  }
+
+  Pen pen(screen, Color(0, 0, 0));
+
+#ifdef MITSHM
+  if (shm_ok) {
+    // use MIT-SHM extension
+    XShmPutImage(pen.XDisplay(), pixmap, pen.gc(), image,
+                 0, 0, 0, 0, width, height, False);
+
+    destroyShmImage(display, image);
+  } else
+#endif // MITSHM
+    {
+      // normal XPutImage
+      XPutImage(pen.XDisplay(), pixmap, pen.gc(), image,
+                0, 0, 0, 0, width, height);
+
+      image->data = 0;
+      XDestroyImage(image);
+    }
+
+  return pixmap;
+}
+
+
+void bt::Image::raisedBevel(unsigned int border_width) {
+  if (width <= 2 || height <= 2 ||
+      width <= (border_width * 4) || height <= (border_width * 4))
+    return;
+
+  RGB *p = data + (border_width * width) + border_width;
+  unsigned int w = width - (border_width * 2);
+  unsigned int h = height - (border_width * 2) - 2;
+  unsigned char rr, gg, bb;
+
+  // top of the bevel
+  do {
+    rr = p->red   + (p->red   >> 1);
+    gg = p->green + (p->green >> 1);
+    bb = p->blue  + (p->blue  >> 1);
+
+    if (rr < p->red  )
+      rr = ~0;
+    if (gg < p->green)
+      gg = ~0;
+    if (bb < p->blue )
+      bb = ~0;
+
+    p->red = rr;
+    p->green = gg;
+    p->blue = bb;
+
+    ++p;
+  } while (--w);
+
+  p += border_width + border_width;
+  w = width - (border_width * 2);
+
+  // left and right of the bevel
+  do {
+    rr = p->red   + (p->red   >> 1);
+    gg = p->green + (p->green >> 1);
+    bb = p->blue  + (p->blue  >> 1);
+
+    if (rr < p->red)
+      rr = ~0;
+    if (gg < p->green)
+      gg = ~0;
+    if (bb < p->blue)
+      bb = ~0;
+
+    p->red = rr;
+    p->green = gg;
+    p->blue = bb;
+
+    p += w - 1;
+
+    rr = (p->red   >> 2) + (p->red   >> 1);
+    gg = (p->green >> 2) + (p->green >> 1);
+    bb = (p->blue  >> 2) + (p->blue  >> 1);
+
+    if (rr > p->red  )
+      rr = 0;
+    if (gg > p->green)
+      gg = 0;
+    if (bb > p->blue )
+      bb = 0;
+
+    p->red   = rr;
+    p->green = gg;
+    p->blue  = bb;
+
+    p += border_width + border_width + 1;
+  } while (--h);
+
+  w = width - (border_width * 2);
+
+  // bottom of the bevel
+  do {
+    rr = (p->red   >> 2) + (p->red   >> 1);
+    gg = (p->green >> 2) + (p->green >> 1);
+    bb = (p->blue  >> 2) + (p->blue  >> 1);
+
+    if (rr > p->red  )
+      rr = 0;
+    if (gg > p->green)
+      gg = 0;
+    if (bb > p->blue )
+      bb = 0;
+
+    p->red   = rr;
+    p->green = gg;
+    p->blue  = bb;
+
+    ++p;
+  } while (--w);
+}
+
+
+void bt::Image::sunkenBevel(unsigned int border_width) {
+  if (width <= 2 || height <= 2 ||
+      width <= (border_width * 4) || height <= (border_width * 4))
+    return;
+
+  RGB *p = data + (border_width * width) + border_width;
+  unsigned int w = width - (border_width * 2);
+  unsigned int h = height - (border_width * 2) - 2;
+  unsigned char rr, gg, bb;
+
+  // top of the bevel
+  do {
+    rr = (p->red   >> 2) + (p->red   >> 1);
+    gg = (p->green >> 2) + (p->green >> 1);
+    bb = (p->blue  >> 2) + (p->blue  >> 1);
+
+    if (rr > p->red  )
+      rr = 0;
+    if (gg > p->green)
+      gg = 0;
+    if (bb > p->blue )
+      bb = 0;
+
+    p->red = rr;
+    p->green = gg;
+    p->blue = bb;
+
+    ++p;
+  } while (--w);
+
+  p += border_width + border_width;
+  w = width - (border_width * 2);
+
+  // left and right of the bevel
+  do {
+    rr = (p->red   >> 2) + (p->red   >> 1);
+    gg = (p->green >> 2) + (p->green >> 1);
+    bb = (p->blue  >> 2) + (p->blue  >> 1);
+
+    if (rr > p->red  )
+      rr = 0;
+    if (gg > p->green)
+      gg = 0;
+    if (bb > p->blue )
+      bb = 0;
+
+    p->red = rr;
+    p->green = gg;
+    p->blue = bb;
+
+    p += w - 1;
+
+    rr = p->red   + (p->red   >> 1);
+    gg = p->green + (p->green >> 1);
+    bb = p->blue  + (p->blue  >> 1);
+
+    if (rr < p->red)
+      rr = ~0;
+    if (gg < p->green)
+      gg = ~0;
+    if (bb < p->blue)
+      bb = ~0;
+
+    p->red   = rr;
+    p->green = gg;
+    p->blue  = bb;
+
+    p += border_width + border_width + 1;
+  } while (--h);
+
+  w = width - (border_width * 2);
+
+  // bottom of the bevel
+  do {
+    rr = p->red   + (p->red   >> 1);
+    gg = p->green + (p->green >> 1);
+    bb = p->blue  + (p->blue  >> 1);
+
+    if (rr < p->red)
+      rr = ~0;
+    if (gg < p->green)
+      gg = ~0;
+    if (bb < p->blue)
+      bb = ~0;
+
+    p->red   = rr;
+    p->green = gg;
+    p->blue  = bb;
+
+    ++p;
+  } while (--w);
+}
+
+
+void bt::Image::dgradient(const Color &from, const Color &to,
+                          bool interlaced) {
+  // diagonal gradient code was written by Mike Cole <mike@mydot.com>
+  // modified for interlacing by Brad Hughes
+
+  double drx, dgx, dbx, dry, dgy, dby;
+  double yr = 0.0, yg = 0.0, yb = 0.0,
+         xr = static_cast<double>(from.red()),
+         xg = static_cast<double>(from.green()),
+         xb = static_cast<double>(from.blue());
+
+  RGB *p = data;
+  unsigned int w = width * 2, h = height * 2;
+  unsigned int x, y;
+
+  const unsigned int dimension = std::max(width, height);
+  unsigned int *alloc = new unsigned int[dimension * 6];
+  unsigned int *xt[3], *yt[3];
+  xt[0] = alloc + (dimension * 0);
+  xt[1] = alloc + (dimension * 1);
+  xt[2] = alloc + (dimension * 2);
+  yt[0] = alloc + (dimension * 3);
+  yt[1] = alloc + (dimension * 4);
+  yt[2] = alloc + (dimension * 5);
+
+  dry = drx = static_cast<double>(to.red()   - from.red());
+  dgy = dgx = static_cast<double>(to.green() - from.green());
+  dby = dbx = static_cast<double>(to.blue()  - from.blue());
+
+  // Create X table
+  drx /= w;
+  dgx /= w;
+  dbx /= w;
+
+  for (x = 0; x < width; ++x) {
+    xt[0][x] = static_cast<unsigned char>(xr);
+    xt[1][x] = static_cast<unsigned char>(xg);
+    xt[2][x] = static_cast<unsigned char>(xb);
+
+    xr += drx;
+    xg += dgx;
+    xb += dbx;
+  }
+
+  // Create Y table
+  dry /= h;
+  dgy /= h;
+  dby /= h;
+
+  for (y = 0; y < height; ++y) {
+    yt[0][y] = static_cast<unsigned char>(yr);
+    yt[1][y] = static_cast<unsigned char>(yg);
+    yt[2][y] = static_cast<unsigned char>(yb);
+
+    yr += dry;
+    yg += dgy;
+    yb += dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (!interlaced) {
+    // normal dgradient
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red   = xt[0][x] + yt[0][y];
+        p->green = xt[1][x] + yt[1][y];
+        p->blue  = xt[2][x] + yt[2][y];
+      }
+    }
+  } else {
+    // interlacing effect
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red   = xt[0][x] + yt[0][y];
+        p->green = xt[1][x] + yt[1][y];
+        p->blue  = xt[2][x] + yt[2][y];
+
+        if (y & 1) {
+          p->red   = (p->red   >> 1) + (p->red   >> 2);
+          p->green = (p->green >> 1) + (p->green >> 2);
+          p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+        }
+      }
+    }
+  }
+
+  delete [] alloc;
+}
+
+
+void bt::Image::hgradient(const Color &from, const Color &to,
+                          bool interlaced) {
+  double drx, dgx, dbx,
+    xr = static_cast<double>(from.red()),
+    xg = static_cast<double>(from.green()),
+    xb = static_cast<double>(from.blue());
+  RGB *p = data;
+  unsigned int total = width * (height - 2);
+  unsigned int x;
+
+  drx = static_cast<double>(to.red()   - from.red());
+  dgx = static_cast<double>(to.green() - from.green());
+  dbx = static_cast<double>(to.blue()  - from.blue());
+
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  if (interlaced && height > 1) {
+    // interlacing effect
+
+    // first line
+    for (x = 0; x < width; ++x, ++p) {
+      p->red   = static_cast<unsigned char>(xr);
+      p->green = static_cast<unsigned char>(xg);
+      p->blue  = static_cast<unsigned char>(xb);
+
+      xr += drx;
+      xg += dgx;
+      xb += dbx;
+    }
+
+    // second line
+    xr = static_cast<double>(from.red()),
+    xg = static_cast<double>(from.green()),
+    xb = static_cast<double>(from.blue());
+
+    for (x = 0; x < width; ++x, ++p) {
+      p->red   = static_cast<unsigned char>(xr);
+      p->green = static_cast<unsigned char>(xg);
+      p->blue  = static_cast<unsigned char>(xb);
+
+      p->red   = (p->red   >> 1) + (p->red   >> 2);
+      p->green = (p->green >> 1) + (p->green >> 2);
+      p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+
+      xr += drx;
+      xg += dgx;
+      xb += dbx;
+    }
+  } else {
+    // first line
+    for (x = 0; x < width; ++x, ++p) {
+      p->red   = static_cast<unsigned char>(xr);
+      p->green = static_cast<unsigned char>(xg);
+      p->blue  = static_cast<unsigned char>(xb);
+
+      xr += drx;
+      xg += dgx;
+      xb += dbx;
+    }
+
+    if (height > 1) {
+      // second line
+      memcpy(p, data, width * sizeof(RGB));
+      p += width;
+    }
+  }
+
+  if (height > 2) {
+    // rest of the gradient
+    for (x = 0; x < total; ++x)
+      p[x] = data[x];
+  }
+}
+
+
+void bt::Image::vgradient(const Color &from, const Color &to,
+                          bool interlaced) {
+  double dry, dgy, dby,
+    yr = static_cast<double>(from.red()  ),
+    yg = static_cast<double>(from.green()),
+    yb = static_cast<double>(from.blue() );
+  RGB *p = data;
+  unsigned int x, y;
+
+  dry = static_cast<double>(to.red()   - from.red()  );
+  dgy = static_cast<double>(to.green() - from.green());
+  dby = static_cast<double>(to.blue()  - from.blue() );
+
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  if (interlaced) {
+    // faked interlacing effect
+    for (y = 0; y < height; ++y) {
+      const RGB rgb = {
+        static_cast<unsigned char>((y & 1) ? (yr * 3. / 4.) : yr),
+        static_cast<unsigned char>((y & 1) ? (yg * 3. / 4.) : yg),
+        static_cast<unsigned char>((y & 1) ? (yb * 3. / 4.) : yb),
+        0
+      };
+      for (x = 0; x < width; ++x, ++p)
+        *p = rgb;
+
+      yr += dry;
+      yg += dgy;
+      yb += dby;
+    }
+  } else {
+    // normal vgradient
+    for (y = 0; y < height; ++y) {
+      const RGB rgb = {
+        static_cast<unsigned char>(yr),
+        static_cast<unsigned char>(yg),
+        static_cast<unsigned char>(yb),
+        0
+      };
+      for (x = 0; x < width; ++x, ++p)
+        *p = rgb;
+
+      yr += dry;
+      yg += dgy;
+      yb += dby;
+    }
+  }
+}
+
+
+void bt::Image::pgradient(const Color &from, const Color &to,
+                          bool interlaced) {
+  // pyramid gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  double yr, yg, yb, drx, dgx, dbx, dry, dgy, dby, xr, xg, xb;
+  int rsign, gsign, bsign;
+  RGB *p = data;
+  unsigned int tr = to.red(), tg = to.green(), tb = to.blue();
+  unsigned int x, y;
+
+  const unsigned int dimension = std::max(width, height);
+  unsigned int *alloc = new unsigned int[dimension * 6];
+  unsigned int *xt[3], *yt[3];
+  xt[0] = alloc + (dimension * 0);
+  xt[1] = alloc + (dimension * 1);
+  xt[2] = alloc + (dimension * 2);
+  yt[0] = alloc + (dimension * 3);
+  yt[1] = alloc + (dimension * 4);
+  yt[2] = alloc + (dimension * 5);
+
+  dry = drx = static_cast<double>(to.red()   - from.red());
+  dgy = dgx = static_cast<double>(to.green() - from.green());
+  dby = dbx = static_cast<double>(to.blue()  - from.blue());
+
+  rsign = (drx < 0) ? -1 : 1;
+  gsign = (dgx < 0) ? -1 : 1;
+  bsign = (dbx < 0) ? -1 : 1;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; ++x) {
+    xt[0][x] = static_cast<unsigned char>(fabs(xr));
+    xt[1][x] = static_cast<unsigned char>(fabs(xg));
+    xt[2][x] = static_cast<unsigned char>(fabs(xb));
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; ++y) {
+    yt[0][y] = static_cast<unsigned char>(fabs(yr));
+    yt[1][y] = static_cast<unsigned char>(fabs(yg));
+    yt[2][y] = static_cast<unsigned char>(fabs(yb));
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (!interlaced) {
+    // normal pgradient
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red =
+          static_cast<unsigned char>(tr - (rsign * (xt[0][x] + yt[0][y])));
+        p->green =
+          static_cast<unsigned char>(tg - (gsign * (xt[1][x] + yt[1][y])));
+        p->blue =
+          static_cast<unsigned char>(tb - (bsign * (xt[2][x] + yt[2][y])));
+      }
+    }
+  } else {
+    // interlacing effect
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red =
+          static_cast<unsigned char>(tr - (rsign * (xt[0][x] + yt[0][y])));
+        p->green =
+          static_cast<unsigned char>(tg - (gsign * (xt[1][x] + yt[1][y])));
+        p->blue =
+          static_cast<unsigned char>(tb - (bsign * (xt[2][x] + yt[2][y])));
+
+        if (y & 1) {
+          p->red   = (p->red   >> 1) + (p->red   >> 2);
+          p->green = (p->green >> 1) + (p->green >> 2);
+          p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+        }
+      }
+    }
+  }
+
+  delete [] alloc;
+}
+
+
+void bt::Image::rgradient(const Color &from, const Color &to,
+                          bool interlaced) {
+  // rectangle gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  double drx, dgx, dbx, dry, dgy, dby, xr, xg, xb, yr, yg, yb;
+  int rsign, gsign, bsign;
+  RGB *p = data;
+  unsigned int tr = to.red(), tg = to.green(), tb = to.blue();
+  unsigned int x, y;
+
+  const unsigned int dimension = std::max(width, height);
+  unsigned int *alloc = new unsigned int[dimension * 6];
+  unsigned int *xt[3], *yt[3];
+  xt[0] = alloc + (dimension * 0);
+  xt[1] = alloc + (dimension * 1);
+  xt[2] = alloc + (dimension * 2);
+  yt[0] = alloc + (dimension * 3);
+  yt[1] = alloc + (dimension * 4);
+  yt[2] = alloc + (dimension * 5);
+
+  dry = drx = static_cast<double>(to.red()   - from.red());
+  dgy = dgx = static_cast<double>(to.green() - from.green());
+  dby = dbx = static_cast<double>(to.blue()  - from.blue());
+
+  rsign = (drx < 0) ? -2 : 2;
+  gsign = (dgx < 0) ? -2 : 2;
+  bsign = (dbx < 0) ? -2 : 2;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; ++x) {
+    xt[0][x] = static_cast<unsigned char>(fabs(xr));
+    xt[1][x] = static_cast<unsigned char>(fabs(xg));
+    xt[2][x] = static_cast<unsigned char>(fabs(xb));
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; ++y) {
+    yt[0][y] = static_cast<unsigned char>(fabs(yr));
+    yt[1][y] = static_cast<unsigned char>(fabs(yg));
+    yt[2][y] = static_cast<unsigned char>(fabs(yb));
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (!interlaced) {
+    // normal rgradient
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red =
+          static_cast<unsigned char>(tr - (rsign *
+                                           std::max(xt[0][x], yt[0][y])));
+        p->green =
+          static_cast<unsigned char>(tg - (gsign *
+                                           std::max(xt[1][x], yt[1][y])));
+        p->blue =
+          static_cast<unsigned char>(tb - (bsign *
+                                           std::max(xt[2][x], yt[2][y])));
+      }
+    }
+  } else {
+    // interlacing effect
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red =
+          static_cast<unsigned char>(tr - (rsign *
+                                           std::max(xt[0][x], yt[0][y])));
+        p->green =
+          static_cast<unsigned char>(tg - (gsign *
+                                           std::max(xt[1][x], yt[1][y])));
+        p->blue =
+          static_cast<unsigned char>(tb - (bsign *
+                                           std::max(xt[2][x], yt[2][y])));
+
+        if (y & 1) {
+          p->red   = (p->red   >> 1) + (p->red   >> 2);
+          p->green = (p->green >> 1) + (p->green >> 2);
+          p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+        }
+      }
+    }
+  }
+
+  delete [] alloc;
+}
+
+
+void bt::Image::egradient(const Color &from, const Color &to,
+                          bool interlaced) {
+  // elliptic gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  double drx, dgx, dbx, dry, dgy, dby, yr, yg, yb, xr, xg, xb;
+  int rsign, gsign, bsign;
+  RGB *p = data;
+  unsigned int tr = to.red(), tg = to.green(), tb = to.blue();
+  unsigned int x, y;
+
+  const unsigned int dimension = std::max(width, height);
+  unsigned int *alloc = new unsigned int[dimension * 6];
+  unsigned int *xt[3], *yt[3];
+  xt[0] = alloc + (dimension * 0);
+  xt[1] = alloc + (dimension * 1);
+  xt[2] = alloc + (dimension * 2);
+  yt[0] = alloc + (dimension * 3);
+  yt[1] = alloc + (dimension * 4);
+  yt[2] = alloc + (dimension * 5);
+
+  dry = drx = static_cast<double>(to.red() - from.red());
+  dgy = dgx = static_cast<double>(to.green() - from.green());
+  dby = dbx = static_cast<double>(to.blue() - from.blue());
+
+  rsign = (drx < 0) ? -1 : 1;
+  gsign = (dgx < 0) ? -1 : 1;
+  bsign = (dbx < 0) ? -1 : 1;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; x++) {
+    xt[0][x] = static_cast<unsigned int>(xr * xr);
+    xt[1][x] = static_cast<unsigned int>(xg * xg);
+    xt[2][x] = static_cast<unsigned int>(xb * xb);
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; y++) {
+    yt[0][y] = static_cast<unsigned int>(yr * yr);
+    yt[1][y] = static_cast<unsigned int>(yg * yg);
+    yt[2][y] = static_cast<unsigned int>(yb * yb);
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (!interlaced) {
+    // normal egradient
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red   = static_cast<unsigned char>
+                   (tr - (rsign * static_cast<int>(sqrt(xt[0][x] +
+                                                        yt[0][y]))));
+        p->green = static_cast<unsigned char>
+                   (tg - (gsign * static_cast<int>(sqrt(xt[1][x] +
+                                                        yt[1][y]))));
+        p->blue  = static_cast<unsigned char>
+                   (tb - (bsign * static_cast<int>(sqrt(xt[2][x] +
+                                                        yt[2][y]))));
+      }
+    }
+  } else {
+    // interlacing effect
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red   = static_cast<unsigned char>
+                   (tr - (rsign * static_cast<int>(sqrt(xt[0][x]
+                                                        + yt[0][y]))));
+        p->green = static_cast<unsigned char>
+                   (tg - (gsign * static_cast<int>(sqrt(xt[1][x]
+                                                        + yt[1][y]))));
+        p->blue  = static_cast<unsigned char>
+                   (tb - (bsign * static_cast<int>(sqrt(xt[2][x]
+                                                        + yt[2][y]))));
+
+        if (y & 1) {
+          p->red   = (p->red   >> 1) + (p->red   >> 2);
+          p->green = (p->green >> 1) + (p->green >> 2);
+          p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+        }
+      }
+    }
+  }
+
+  delete [] alloc;
+}
+
+
+void bt::Image::pcgradient(const Color &from, const Color &to,
+                           bool interlaced) {
+  // pipe cross gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  double drx, dgx, dbx, dry, dgy, dby, xr, xg, xb, yr, yg, yb;
+  int rsign, gsign, bsign;
+  RGB *p = data;
+  unsigned int tr = to.red(), tg = to.green(), tb = to.blue();
+  unsigned int x, y;
+
+  const unsigned int dimension = std::max(width, height);
+  unsigned int *alloc = new unsigned int[dimension * 6];
+  unsigned int *xt[3], *yt[3];
+  xt[0] = alloc + (dimension * 0);
+  xt[1] = alloc + (dimension * 1);
+  xt[2] = alloc + (dimension * 2);
+  yt[0] = alloc + (dimension * 3);
+  yt[1] = alloc + (dimension * 4);
+  yt[2] = alloc + (dimension * 5);
+
+  dry = drx = static_cast<double>(to.red()   - from.red());
+  dgy = dgx = static_cast<double>(to.green() - from.green());
+  dby = dbx = static_cast<double>(to.blue()  - from.blue());
+
+  rsign = (drx < 0) ? -2 : 2;
+  gsign = (dgx < 0) ? -2 : 2;
+  bsign = (dbx < 0) ? -2 : 2;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; ++x) {
+    xt[0][x] = static_cast<unsigned char>(fabs(xr));
+    xt[1][x] = static_cast<unsigned char>(fabs(xg));
+    xt[2][x] = static_cast<unsigned char>(fabs(xb));
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; ++y) {
+    yt[0][y] = static_cast<unsigned char>(fabs(yr));
+    yt[1][y] = static_cast<unsigned char>(fabs(yg));
+    yt[2][y] = static_cast<unsigned char>(fabs(yb));
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (!interlaced) {
+    // normal rgradient
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red =
+          static_cast<unsigned char>(tr - (rsign *
+                                           std::min(xt[0][x], yt[0][y])));
+        p->green =
+          static_cast<unsigned char>(tg - (gsign *
+                                           std::min(xt[1][x], yt[1][y])));
+        p->blue =
+          static_cast<unsigned char>(tb - (bsign *
+                                           std::min(xt[2][x], yt[2][y])));
+      }
+    }
+  } else {
+    // interlacing effect
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red =
+          static_cast<unsigned char>(tr - (rsign *
+                                           std::min(xt[0][x], yt[0][y])));
+        p->green =
+          static_cast<unsigned char>(tg - (gsign *
+                                           std::min(xt[1][x], yt[1][y])));
+        p->blue =
+          static_cast<unsigned char>(tb - (bsign *
+                                           std::min(xt[2][x], yt[2][y])));
+
+        if (y & 1) {
+          p->red   = (p->red   >> 1) + (p->red   >> 2);
+          p->green = (p->green >> 1) + (p->green >> 2);
+          p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+        }
+      }
+    }
+  }
+
+  delete [] alloc;
+}
+
+
+void bt::Image::cdgradient(const Color &from, const Color &to,
+                           bool interlaced) {
+  // cross diagonal gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  double drx, dgx, dbx, dry, dgy, dby;
+  double yr = 0.0, yg = 0.0, yb = 0.0,
+         xr = static_cast<double>(from.red()  ),
+         xg = static_cast<double>(from.green()),
+         xb = static_cast<double>(from.blue() );
+  RGB *p = data;
+  unsigned int w = width * 2, h = height * 2;
+  unsigned int x, y;
+
+  const unsigned int dimension = std::max(width, height);
+  unsigned int *alloc = new unsigned int[dimension * 6];
+  unsigned int *xt[3], *yt[3];
+  xt[0] = alloc + (dimension * 0);
+  xt[1] = alloc + (dimension * 1);
+  xt[2] = alloc + (dimension * 2);
+  yt[0] = alloc + (dimension * 3);
+  yt[1] = alloc + (dimension * 4);
+  yt[2] = alloc + (dimension * 5);
+
+  dry = drx = static_cast<double>(to.red()   - from.red()  );
+  dgy = dgx = static_cast<double>(to.green() - from.green());
+  dby = dbx = static_cast<double>(to.blue()  - from.blue() );
+
+  // Create X table
+  drx /= w;
+  dgx /= w;
+  dbx /= w;
+
+  for (x = width - 1; x != 0; --x) {
+    xt[0][x] = static_cast<unsigned char>(xr);
+    xt[1][x] = static_cast<unsigned char>(xg);
+    xt[2][x] = static_cast<unsigned char>(xb);
+
+    xr += drx;
+    xg += dgx;
+    xb += dbx;
+  }
+
+  xt[0][x] = static_cast<unsigned char>(xr);
+  xt[1][x] = static_cast<unsigned char>(xg);
+  xt[2][x] = static_cast<unsigned char>(xb);
+
+  // Create Y table
+  dry /= h;
+  dgy /= h;
+  dby /= h;
+
+  for (y = 0; y < height; ++y) {
+    yt[0][y] = static_cast<unsigned char>(yr);
+    yt[1][y] = static_cast<unsigned char>(yg);
+    yt[2][y] = static_cast<unsigned char>(yb);
+
+    yr += dry;
+    yg += dgy;
+    yb += dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (!interlaced) {
+    // normal dgradient
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red   = xt[0][x] + yt[0][y];
+        p->green = xt[1][x] + yt[1][y];
+        p->blue  = xt[2][x] + yt[2][y];
+      }
+    }
+  } else {
+    // interlacing effect
+    for (y = 0; y < height; ++y) {
+      for (x = 0; x < width; ++x, ++p) {
+        p->red   = xt[0][x] + yt[0][y];
+        p->green = xt[1][x] + yt[1][y];
+        p->blue  = xt[2][x] + yt[2][y];
+
+        if (y & 1) {
+          p->red   = (p->red   >> 1) + (p->red   >> 2);
+          p->green = (p->green >> 1) + (p->green >> 2);
+          p->blue  = (p->blue  >> 1) + (p->blue  >> 2);
+        }
+      }
+    }
+  }
+
+  delete [] alloc;
+}
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/Resource.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/Resource.cc
new file mode 100644
index 0000000..8240719
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/Resource.cc
@@ -0,0 +1,218 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Resource.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "Resource.hh"
+#include "Util.hh"
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+
+#include <stdio.h>
+
+
+bt::Resource::Resource(void)
+  : db(NULL)
+{ }
+
+
+bt::Resource::Resource(const std::string &filename)
+  : db(NULL)
+{ load(filename); }
+
+
+bt::Resource::~Resource(void)
+{ XrmDestroyDatabase(db); }
+
+
+void bt::Resource::load(const std::string &filename)
+{
+  XrmDestroyDatabase(db);
+  if (!filename.empty())
+    db = XrmGetFileDatabase(expandTilde(filename).c_str());
+  else
+    db = NULL;
+}
+
+
+void bt::Resource::save(const std::string &filename)
+{
+  if (!valid() || filename.empty())
+    return;
+  XrmPutFileDatabase(db, expandTilde(filename).c_str());
+}
+
+
+void bt::Resource::merge(const std::string &filename)
+{
+  if (filename.empty())
+    return;
+  XrmCombineFileDatabase(expandTilde(filename).c_str(), &db, false);
+}
+
+
+std::string bt::Resource::read(const char* name, const char* classname,
+                               const char* default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value))
+    return std::string(value.addr, value.size - 1);
+  return std::string(default_value);
+}
+
+
+std::string bt::Resource::read(const std::string& name,
+                               const std::string& classname,
+                               const std::string& default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name.c_str(), classname.c_str(),
+                     &value_type, &value))
+    return std::string(value.addr, value.size - 1);
+  return default_value;
+}
+
+
+int bt::Resource::read(const char* name, const char* classname,
+                       int default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value)) {
+    int output;
+    sscanf(value.addr, "%d", &output);
+    return output;
+  }
+  return default_value;
+}
+
+
+unsigned int bt::Resource::read(const char* name, const char* classname,
+                                unsigned int default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value)) {
+    unsigned int output;
+    sscanf(value.addr, "%u", &output);
+    return output;
+  }
+  return default_value;
+}
+
+
+long bt::Resource::read(const char* name, const char* classname,
+                        long default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value)) {
+    long output;
+    sscanf(value.addr, "%ld", &output);
+    return output;
+  }
+  return default_value;
+}
+
+
+unsigned long bt::Resource::read(const char* name, const char* classname,
+                                 unsigned long default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value)) {
+    unsigned long output;
+    sscanf(value.addr, "%lu", &output);
+    return output;
+  }
+  return default_value;
+}
+
+
+bool bt::Resource::read(const char* name, const char* classname,
+                        bool default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value)) {
+    if (strncasecmp(value.addr, "true", value.size) == 0)
+      return true;
+    return false;
+  }
+  return default_value;
+}
+
+
+double bt::Resource::read(const char* name, const char* classname,
+                          double default_value) const {
+  XrmValue value;
+  char *value_type;
+  if (XrmGetResource(db, name, classname, &value_type, &value)) {
+    double output;
+    sscanf(value.addr, "%lf", &output);
+    return output;
+  }
+  return default_value;
+}
+
+
+void bt::Resource::write(const char *resource, const std::string &value)
+{ write(resource, value.c_str()); }
+
+
+void bt::Resource::write(const char* resource, const char* value)
+{ XrmPutStringResource(&db, resource, value); }
+
+
+void bt::Resource::write(const char* resource, int value) {
+  char tmp[16];
+  sprintf(tmp, "%d", value);
+  write(resource, tmp);
+}
+
+
+void bt::Resource::write(const char* resource, unsigned int value) {
+  char tmp[16];
+  sprintf(tmp, "%u", value);
+  write(resource, tmp);
+}
+
+
+void bt::Resource::write(const char* resource, long value) {
+  char tmp[64];
+  sprintf(tmp, "%ld", value);
+  write(resource, tmp);
+}
+
+
+void bt::Resource::write(const char* resource, unsigned long value) {
+  char tmp[64];
+  sprintf(tmp, "%lu", value);
+  write(resource, tmp);
+}
+
+
+void bt::Resource::write(const char* resource, bool value)
+{ write(resource, boolAsString(value)); }
+
+
+void bt::Resource::write(const char* resource, double value) {
+  char tmp[80];
+  sprintf(tmp, "%f", value);
+  write(resource, tmp);
+}
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/XDG.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/XDG.cc
new file mode 100644
index 0000000..feabb92
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/lib/XDG.cc
@@ -0,0 +1,139 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// XDG.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "Util.hh"
+#include "XDG.hh"
+
+#include <stdlib.h>
+
+
+// make sure directory names end with a slash
+static std::string terminateDir(const std::string &string)
+{
+  std::string returnValue = string;
+  std::string::const_iterator it = returnValue.end() - 1;
+  if (*it != '/')
+    returnValue += '/';
+  return returnValue;
+}
+
+static std::string readEnvDir(const char *name, const char *defaultValue)
+{
+  const char * const env = getenv(name);
+  std::string returnValue = std::string(env ? env : defaultValue);
+  returnValue = bt::expandTilde(returnValue);
+  return terminateDir(returnValue);
+}
+
+static std::list<std::string> readEnvDirList(const char *name,
+                                             const char *defaultValue)
+{
+  const char * const env = getenv(name);
+
+  std::string str = env ? env : defaultValue;
+  // if the environment variable ends with a ':', append the
+  // defaultValue
+  std::string::const_iterator last = str.end() - 1;
+  if (*last == ':')
+    str += defaultValue;
+
+  std::list<std::string> returnValue;
+  const std::string::const_iterator end = str.end();
+  std::string::const_iterator begin = str.begin();
+  do {
+    std::string::const_iterator it = std::find(begin, end, ':');
+    std::string dir = std::string(begin, it);
+    dir = bt::expandTilde(dir);
+    dir = terminateDir(dir);
+    returnValue.push_back(dir);
+    begin = it;
+    if (begin != end)
+      ++begin;
+  } while (begin != end);
+
+  return returnValue;
+}
+
+
+std::string bt::XDG::BaseDir::dataHome()
+{
+  static std::string XDG_DATA_HOME =
+    readEnvDir("XDG_DATA_HOME", "~/.local/share/");
+  return XDG_DATA_HOME;
+}
+
+std::string bt::XDG::BaseDir::configHome()
+{
+  static std::string XDG_CONFIG_HOME =
+    readEnvDir("XDG_CONFIG_HOME", "~/.config/");
+  return XDG_CONFIG_HOME;
+}
+
+std::list<std::string> bt::XDG::BaseDir::dataDirs()
+{
+  static std::list<std::string> XDG_DATA_DIRS =
+    readEnvDirList("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/");
+  return XDG_DATA_DIRS;
+}
+
+std::list<std::string> bt::XDG::BaseDir::configDirs()
+{
+  static std::list<std::string> XDG_CONFIG_DIRS =
+    readEnvDirList("XDG_CONFIG_DIRS", "/etc/xdg/");
+  return XDG_CONFIG_DIRS;
+}
+
+std::string bt::XDG::BaseDir::cacheHome()
+{
+  static std::string XDG_CACHE_HOME =
+    readEnvDir("XDG_CACHE_HOME", "~/.cache/");
+  return XDG_CACHE_HOME;
+}
+
+std::string bt::XDG::BaseDir::writeDataFile(const std::string &filename)
+{
+  std::string path = dataHome() + filename;
+  std::string directoryName = dirname(path);
+  if (!mkdirhier(directoryName, 0700))
+    return std::string();
+  return path;
+}
+
+std::string bt::XDG::BaseDir::writeConfigFile(const std::string &filename)
+{
+  std::string path = configHome() + filename;
+  std::string directoryName = dirname(path);
+  if (!mkdirhier(directoryName, 0700))
+    return std::string();
+  return path;
+}
+
+std::string bt::XDG::BaseDir::writeCacheFile(const std::string &filename)
+{
+  std::string path = cacheHome() + filename;
+  std::string directoryName = dirname(path);
+  if (!mkdirhier(directoryName, 0700))
+    return std::string();
+  return path;
+}
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/src/BlackboxResource.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/BlackboxResource.cc
new file mode 100644
index 0000000..27d1323
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/BlackboxResource.cc
@@ -0,0 +1,327 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// BlackboxResource.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "BlackboxResource.hh"
+
+#include "blackbox.hh"
+
+#include <Image.hh>
+#include <Resource.hh>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+
+
+BlackboxResource::BlackboxResource(const std::string& rc): rc_file(rc) {
+  screen_resources = 0;
+  auto_raise_delay.tv_sec = auto_raise_delay.tv_usec = 0;
+}
+
+
+BlackboxResource::~BlackboxResource(void)
+{ delete [] screen_resources; }
+
+
+void BlackboxResource::load(Blackbox& blackbox) {
+  if (screen_resources == 0) {
+    screen_resources = new ScreenResource[blackbox.screenCount()];
+  }
+
+  bt::Resource res(rc_file);
+
+  menu_file = bt::expandTilde(res.read("session.menuFile",
+                                       "Session.MenuFile",
+                                       DEFAULTMENU));
+
+  style_file = bt::expandTilde(res.read("session.styleFile",
+                                        "Session.StyleFile",
+                                        DEFAULTSTYLE));
+
+  unsigned int maxcolors = res.read("session.maximumColors",
+                                    "Session.MaximumColors",
+                                    ~0u);
+  if (maxcolors != ~0u)
+    bt::Image::setMaximumColors(maxcolors);
+
+  double_click_interval = res.read("session.doubleClickInterval",
+                                   "Session.DoubleClickInterval",
+                                   250l);
+
+  auto_raise_delay.tv_usec = res.read("session.autoRaiseDelay",
+                                      "Session.AutoRaiseDelay",
+                                      400l);
+
+  auto_raise_delay.tv_sec = auto_raise_delay.tv_usec / 1000;
+  auto_raise_delay.tv_usec -= (auto_raise_delay.tv_sec * 1000);
+  auto_raise_delay.tv_usec *= 1000;
+
+  bt::DitherMode dither_mode;
+  std::string str = res.read("session.imageDither",
+                             "Session.ImageDither",
+                             "OrderedDither");
+  if (!strcasecmp("ordered", str.c_str()) ||
+      !strcasecmp("fast", str.c_str()) ||
+      !strcasecmp("ordereddither", str.c_str()) ||
+      !strcasecmp("fastdither", str.c_str())) {
+    dither_mode = bt::OrderedDither;
+  } else if (!strcasecmp("floydsteinberg", str.c_str()) ||
+             !strcasecmp("quality", str.c_str()) ||
+             !strcasecmp("diffuse", str.c_str()) ||
+             !strcasecmp("floydsteinbergdither", str.c_str()) ||
+             !strcasecmp("qualitydither", str.c_str()) ||
+             !strcasecmp("diffusedither", str.c_str())) {
+    dither_mode = bt::FloydSteinbergDither;
+  } else if (!strcasecmp("no", str.c_str()) ||
+             !strcasecmp("nodither", str.c_str()) ||
+             !strcasecmp("off", str.c_str())) {
+    dither_mode = bt::NoDither;
+  } else {
+    dither_mode = bt::OrderedDither;
+  }
+  bt::Image::setDitherMode(dither_mode);
+
+  _cursors.pointer =
+    XCreateFontCursor(blackbox.XDisplay(), XC_left_ptr);
+  _cursors.move =
+    XCreateFontCursor(blackbox.XDisplay(), XC_fleur);
+  _cursors.resize_top_left =
+    XCreateFontCursor(blackbox.XDisplay(), XC_top_left_corner);
+  _cursors.resize_bottom_left =
+    XCreateFontCursor(blackbox.XDisplay(), XC_bottom_left_corner);
+  _cursors.resize_top_right =
+    XCreateFontCursor(blackbox.XDisplay(), XC_top_right_corner);
+  _cursors.resize_bottom_right =
+    XCreateFontCursor(blackbox.XDisplay(), XC_bottom_right_corner);
+
+  // window options
+  str = res.read("session.focusModel",
+                 "Session.FocusModel",
+                 res.read("session.screen0.focusModel",
+                          "Session.Screen0.FocusModel",
+                          "ClickToFocus"));
+  if (str.find("ClickToFocus") != std::string::npos) {
+    focus_model = ClickToFocusModel;
+    auto_raise = false;
+    click_raise = false;
+  } else {
+    focus_model = SloppyFocusModel;
+    auto_raise = (str.find("AutoRaise") != std::string::npos);
+    click_raise = (str.find("ClickRaise") != std::string::npos);
+  }
+
+  str = res.read("session.windowPlacement",
+                 "Session.WindowPlacement",
+                 res.read("session.screen0.windowPlacement",
+                          "Session.Screen0.WindowPlacement",
+                          "RowSmartPlacement"));
+  if (strcasecmp(str.c_str(), "ColSmartPlacement") == 0)
+    window_placement_policy = ColSmartPlacement;
+  else if (strcasecmp(str.c_str(), "CenterPlacement") == 0)
+    window_placement_policy = CenterPlacement;
+  else if (strcasecmp(str.c_str(), "CascadePlacement") == 0)
+    window_placement_policy = CascadePlacement;
+  else
+    window_placement_policy = RowSmartPlacement;
+
+  str = res.read("session.rowPlacementDirection",
+                 "Session.RowPlacementDirection",
+                 res.read("session.screen0.rowPlacementDirection",
+                          "Session.Screen0.RowPlacementDirection",
+                          "lefttoright"));
+  row_direction =
+    (strcasecmp(str.c_str(), "righttoleft") == 0) ? RightLeft : LeftRight;
+
+  str = res.read("session.colPlacementDirection",
+                 "Session.ColPlacementDirection",
+                 res.read("session.screen0.colPlacementDirection",
+                          "Session.Screen0.ColPlacementDirection",
+                          "toptobottom"));
+  col_direction =
+    (strcasecmp(str.c_str(), "bottomtotop") == 0) ? BottomTop : TopBottom;
+
+  ignore_shaded =
+    res.read("session.placementIgnoresShaded",
+             "Session.placementIgnoresShaded",
+             true);
+
+  opaque_move =
+    res.read("session.opaqueMove",
+             "Session.OpaqueMove",
+             true);
+  opaque_resize =
+    res.read("session.opaqueResize",
+             "Session.OpaqueResize",
+             true);
+  full_max =
+    res.read("session.fullMaximization",
+             "Session.FullMaximization",
+             res.read("session.screen0.fullMaximization",
+                      "Session.Screen0.FullMaximization",
+                      false));
+  focus_new_windows =
+    res.read("session.focusNewWindows",
+             "Session.FocusNewWindows",
+             res.read("session.screen0.focusNewWindows",
+                      "Session.Screen0.FocusNewWindows",
+                      true));
+  focus_last_window_on_workspace =
+    res.read("session.focusLastWindow",
+             "Session.focusLastWindow",
+             res.read("session.screen0.focusLastWindow",
+                      "Session.Screen0.focusLastWindow",
+                      true));
+  change_workspace_with_mouse_wheel =
+    res.read("session.changeWorkspaceWithMouseWheel",
+             "session.changeWorkspaceWithMouseWheel",
+             true);
+  shade_window_with_mouse_wheel =
+    res.read("session.shadeWindowWithMouseWheel",
+             "session.shadeWindowWithMouseWheel",
+             true);
+  toolbar_actions_with_mouse_wheel =
+    res.read("session.toolbarActionsWithMouseWheel",
+             "session.toolbarActionsWithMouseWheel",
+             true);
+  allow_scroll_lock =
+    res.read("session.disableBindingsWithScrollLock",
+             "Session.disableBindingsWithScrollLock",
+             res.read("session.screen0.disableBindingsWithScrollLock",
+                      "Session.Screen0.disableBindingsWithScrollLock",
+                      false));
+  edge_snap_threshold =
+    res.read("session.edgeSnapThreshold",
+             "Session.EdgeSnapThreshold",
+             res.read("session.screen0.edgeSnapThreshold",
+                      "Session.Screen0.EdgeSnapThreshold",
+                      0));
+  window_snap_threshold =
+    res.read("session.windowSnapThreshold",
+             "Session.windowSnapThreshold",
+             0);
+
+  for (unsigned int i = 0; i < blackbox.screenCount(); ++i)
+    screen_resources[i].load(res, i);
+}
+
+
+void BlackboxResource::save(Blackbox& blackbox) {
+  bt::Resource res;
+
+  {
+    if (bt::Resource(rc_file).read("session.cacheLife",
+                                   "Session.CacheLife",
+                                   -1) == -1) {
+      res.merge(rc_file);
+    } else {
+      // we are converting from 0.65.0 to 0.70.0, let's take the liberty
+      // of generating a brand new rc file to make sure we throw out
+      // undeeded entries
+    }
+  }
+
+  res.write("session.menuFile", menuFilename());
+
+  res.write("session.styleFile", styleFilename());
+
+  res.write("session.maximumColors",  bt::Image::maximumColors());
+
+  res.write("session.doubleClickInterval", double_click_interval);
+
+  res.write("session.autoRaiseDelay", ((auto_raise_delay.tv_sec * 1000ul) +
+                                       (auto_raise_delay.tv_usec / 1000ul)));
+
+  std::string str;
+  switch (bt::Image::ditherMode()) {
+  case bt::OrderedDither:        str = "OrderedDither";        break;
+  case bt::FloydSteinbergDither: str = "FloydSteinbergDither"; break;
+  default:                       str = "NoDither";             break;
+  }
+  res.write("session.imageDither", str);
+
+  // window options
+  switch (focus_model) {
+  case SloppyFocusModel:
+  default:
+    str = "SloppyFocus";
+    if (auto_raise)
+      str += " AutoRaise";
+    if (click_raise)
+      str += " ClickRaise";
+    break;
+  case ClickToFocusModel:
+    str = "ClickToFocus";
+    break;
+  }
+  res.write("session.focusModel", str);
+
+  switch (window_placement_policy) {
+  case CascadePlacement:
+    str = "CascadePlacement";
+    break;
+  case CenterPlacement:
+    str = "CenterPlacement";
+    break;
+  case ColSmartPlacement:
+    str = "ColSmartPlacement";
+    break;
+  case RowSmartPlacement:
+  default:
+    str = "RowSmartPlacement";
+    break;
+  }
+  res.write("session.windowPlacement", str);
+  res.write("session.rowPlacementDirection",
+            (row_direction == LeftRight)
+            ? "LeftToRight"
+            : "RightToLeft");
+  res.write("session.colPlacementDirection",
+            (col_direction == TopBottom)
+            ? "TopToBottom"
+            : "BottomToTop");
+
+  res.write("session.placementIgnoresShaded", ignore_shaded);
+
+  res.write("session.opaqueMove", opaque_move);
+  res.write("session.opaqueResize", opaque_resize);
+  res.write("session.fullMaximization", full_max);
+  res.write("session.focusNewWindows", focus_new_windows);
+  res.write("session.focusLastWindow", focus_last_window_on_workspace);
+  res.write("session.changeWorkspaceWithMouseWheel",
+            change_workspace_with_mouse_wheel);
+  res.write("session.shadeWindowWithMouseWheel",
+            shade_window_with_mouse_wheel);
+  res.write("session.toolbarActionsWithMouseWheel",
+            toolbar_actions_with_mouse_wheel);
+  res.write("session.disableBindingsWithScrollLock", allow_scroll_lock);
+  res.write("session.edgeSnapThreshold", edge_snap_threshold);
+  res.write("session.windowSnapThreshold", window_snap_threshold);
+
+  for (unsigned int i = 0; i < blackbox.screenCount(); ++i)
+    screen_resources[i].save(res, blackbox.screenNumber(i));
+
+  res.save(rc_file);
+}
+
+
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/src/Screen.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/Screen.cc
new file mode 100644
index 0000000..838d168
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/Screen.cc
@@ -0,0 +1,2486 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Screen.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "Screen.hh"
+#include "Clientmenu.hh"
+#include "Configmenu.hh"
+#include "Iconmenu.hh"
+#include "Rootmenu.hh"
+#include "Slit.hh"
+#include "Slitmenu.hh"
+#include "Toolbar.hh"
+#include "Toolbarmenu.hh"
+#include "Window.hh"
+#include "WindowGroup.hh"
+#include "Windowmenu.hh"
+#include "Workspace.hh"
+#include "Workspacemenu.hh"
+
+#include <Pen.hh>
+#include <PixmapCache.hh>
+#include <Unicode.hh>
+
+#include <X11/Xutil.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+
+
+static bool running = true;
+static int anotherWMRunning(Display *, XErrorEvent *) {
+  running = false;
+  return -1;
+}
+
+
+BScreen::BScreen(Blackbox *bb, unsigned int scrn) :
+  screen_info(bb->display().screenInfo(scrn)), _blackbox(bb),
+  _resource(bb->resource().screenResource(scrn))
+{
+  running = true;
+  XErrorHandler old = XSetErrorHandler((XErrorHandler) anotherWMRunning);
+  XSelectInput(screen_info.display().XDisplay(),
+               screen_info.rootWindow(),
+               PropertyChangeMask |
+               StructureNotifyMask |
+               SubstructureRedirectMask |
+	       ButtonPressMask);
+
+  XSync(screen_info.display().XDisplay(), False);
+  XSetErrorHandler((XErrorHandler) old);
+
+  managed = running;
+  if (! managed) {
+    fprintf(stderr,
+            "%s: another window manager is already running on display '%s'\n",
+            _blackbox->applicationName().c_str(),
+            DisplayString(_blackbox->XDisplay()));
+    return;
+  }
+
+  static const char *visual_classes[] = {
+    "StaticGray",
+    "GrayScale",
+    "StaticColor",
+    "PseudoColor",
+    "TrueColor",
+    "DirectColor"
+  };
+  printf("%s: managing screen %u using %s visual 0x%lx, depth %d\n",
+         _blackbox->applicationName().c_str(), screen_info.screenNumber(),
+         visual_classes[screen_info.visual()->c_class],
+         XVisualIDFromVisual(screen_info.visual()), screen_info.depth());
+
+  _blackbox->insertEventHandler(screen_info.rootWindow(), this);
+
+  cascade_x = cascade_y = ~0;
+
+  _rootmenu = 0;
+  _windowmenu = 0;
+
+  XDefineCursor(_blackbox->XDisplay(), screen_info.rootWindow(),
+                _blackbox->resource().cursors().pointer);
+
+  // start off full screen, top left.
+  usableArea.setSize(screen_info.width(), screen_info.height());
+
+  LoadStyle();
+
+  geom_pixmap = None;
+  geom_visible = False;
+  geom_window = None;
+  updateGeomWindow();
+
+  empty_window =
+    XCreateSimpleWindow(_blackbox->XDisplay(), screen_info.rootWindow(),
+                        0, 0, screen_info.width(), screen_info.height(), 0,
+                        0l, 0l);
+  XSetWindowBackgroundPixmap(_blackbox->XDisplay(), empty_window, None);
+
+  no_focus_window =
+    XCreateSimpleWindow(_blackbox->XDisplay(), screen_info.rootWindow(),
+                        screen_info.width(), screen_info.height(), 1, 1,
+                        0, 0l, 0l);
+  XSelectInput(_blackbox->XDisplay(), no_focus_window, NoEventMask);
+  XMapWindow(_blackbox->XDisplay(), no_focus_window);
+
+  _iconmenu =
+    new Iconmenu(*_blackbox, screen_info.screenNumber(), this);
+  _slitmenu =
+    new Slitmenu(*_blackbox, screen_info.screenNumber(), this);
+  _toolbarmenu =
+    new Toolbarmenu(*_blackbox, screen_info.screenNumber(), this);
+  _workspacemenu =
+    new Workspacemenu(*_blackbox, screen_info.screenNumber(), this);
+
+  configmenu =
+    new Configmenu(*_blackbox, screen_info.screenNumber(), this);
+
+  if (_resource.workspaceCount() == 0) // there is always 1 workspace
+    _resource.setWorkspaceCount(1);
+
+  _workspacemenu->insertIconMenu(_iconmenu);
+  for (unsigned int i = 0; i < _resource.workspaceCount(); ++i) {
+    Workspace *wkspc = new Workspace(this, i);
+    workspacesList.push_back(wkspc);
+    _workspacemenu->insertWorkspace(wkspc);
+  }
+
+  current_workspace = workspacesList.front()->id();
+  _workspacemenu->setWorkspaceChecked(current_workspace, true);
+
+  // the Slit will be created on demand
+  _slit = 0;
+
+  _toolbar = 0;
+  if (_resource.toolbarOptions().enabled) {
+    _toolbar = new Toolbar(this);
+    _stackingList.insert(_toolbar);
+  }
+
+  InitMenu();
+
+  const bt::EWMH& ewmh = _blackbox->ewmh();
+  /*
+    ewmh requires the window manager to set a property on a window it creates
+    which is the id of that window.  We must also set an equivalent property
+    on the root window.  Then we must set _NET_WM_NAME on the child window
+    to be the name of the wm.
+  */
+  ewmh.setSupportingWMCheck(screen_info.rootWindow(), geom_window);
+  ewmh.setSupportingWMCheck(geom_window, geom_window);
+  ewmh.setWMName(geom_window, bt::toUnicode("Blackbox"));
+
+  ewmh.setCurrentDesktop(screen_info.rootWindow(), 0);
+  ewmh.setNumberOfDesktops(screen_info.rootWindow(),
+                           workspacesList.size());
+  ewmh.setDesktopGeometry(screen_info.rootWindow(),
+                          screen_info.width(), screen_info.height());
+  ewmh.setDesktopViewport(screen_info.rootWindow(), 0, 0);
+  ewmh.setActiveWindow(screen_info.rootWindow(), None);
+  updateWorkareaHint();
+  updateDesktopNamesHint();
+
+  Atom supported[] = {
+    ewmh.clientList(),
+    ewmh.clientListStacking(),
+    ewmh.numberOfDesktops(),
+    // _NET_DESKTOP_GEOMETRY is not supported
+    // _NET_DESKTOP_VIEWPORT is not supported
+    ewmh.currentDesktop(),
+    ewmh.desktopNames(),
+    ewmh.activeWindow(),
+    ewmh.workarea(),
+    // _NET_VIRTUAL_ROOTS   is not supported
+    // _NET_SHOWING_DESKTOP is not supported
+
+    ewmh.closeWindow(),
+    ewmh.moveresizeWindow(),
+    // _NET_WM_MOVERESIZE is not supported
+
+    ewmh.wmName(),
+    ewmh.wmVisibleName(),
+    ewmh.wmIconName(),
+    ewmh.wmVisibleIconName(),
+    ewmh.wmDesktop(),
+
+    ewmh.wmWindowType(),
+    ewmh.wmWindowTypeDesktop(),
+    ewmh.wmWindowTypeDock(),
+    ewmh.wmWindowTypeToolbar(),
+    ewmh.wmWindowTypeMenu(),
+    ewmh.wmWindowTypeUtility(),
+    ewmh.wmWindowTypeSplash(),
+    ewmh.wmWindowTypeDialog(),
+    ewmh.wmWindowTypeNormal(),
+
+    ewmh.wmState(),
+    ewmh.wmStateModal(),
+    // _NET_WM_STATE_STICKY is not supported
+    ewmh.wmStateMaximizedVert(),
+    ewmh.wmStateMaximizedHorz(),
+    ewmh.wmStateShaded(),
+    ewmh.wmStateSkipTaskbar(),
+    ewmh.wmStateSkipPager(),
+    ewmh.wmStateHidden(),
+    ewmh.wmStateFullscreen(),
+    ewmh.wmStateAbove(),
+    ewmh.wmStateBelow(),
+
+    ewmh.wmAllowedActions(),
+    ewmh.wmActionMove(),
+    ewmh.wmActionResize(),
+    ewmh.wmActionMinimize(),
+    ewmh.wmActionShade(),
+    // _NET_WM_ACTION_STICK is not supported
+    ewmh.wmActionMaximizeHorz(),
+    ewmh.wmActionMaximizeVert(),
+    ewmh.wmActionFullscreen(),
+    ewmh.wmActionChangeDesktop(),
+    ewmh.wmActionClose(),
+
+    ewmh.wmStrut()
+    // _NET_WM_STRUT_PARTIAL is not supported
+    // _NET_WM_ICON_GEOMETRY is not supported
+    // _NET_WM_ICON          is not supported
+    // _NET_WM_PID           is not supported
+    // _NET_WM_HANDLED_ICONS is not supported
+    // _NET_WM_USER_TIME     is not supported
+
+    // _NET_WM_PING          is not supported
+  };
+
+  ewmh.setSupported(screen_info.rootWindow(), supported,
+                     sizeof(supported) / sizeof(Atom));
+
+  _blackbox->XGrabServer();
+
+  unsigned int i, j, nchild;
+  Window r, p, *children;
+  XQueryTree(_blackbox->XDisplay(), screen_info.rootWindow(), &r, &p,
+             &children, &nchild);
+
+  // preen the window list of all icon windows... for better dockapp support
+  for (i = 0; i < nchild; i++) {
+    if (children[i] == None || children[i] == no_focus_window)
+      continue;
+
+    XWMHints *wmhints = XGetWMHints(_blackbox->XDisplay(),
+                                    children[i]);
+
+    if (wmhints) {
+      if ((wmhints->flags & IconWindowHint) &&
+          (wmhints->icon_window != children[i])) {
+        for (j = 0; j < nchild; j++) {
+          if (children[j] == wmhints->icon_window) {
+            children[j] = None;
+            break;
+          }
+        }
+      }
+
+      XFree(wmhints);
+    }
+  }
+
+  // manage shown windows
+  for (i = 0; i < nchild; ++i) {
+    if (children[i] == None || children[i] == no_focus_window)
+      continue;
+
+    XWindowAttributes attrib;
+    if (XGetWindowAttributes(_blackbox->XDisplay(), children[i], &attrib)) {
+      if (attrib.override_redirect) continue;
+
+      if (attrib.map_state != IsUnmapped) {
+        manageWindow(children[i]);
+      }
+    }
+  }
+
+  XFree(children);
+
+  _blackbox->XUngrabServer();
+
+  updateClientListHint();
+  restackWindows();
+}
+
+
+BScreen::~BScreen(void) {
+  if (! managed) return;
+
+  _blackbox->removeEventHandler(screen_info.rootWindow());
+
+  bt::PixmapCache::release(geom_pixmap);
+
+  if (geom_window != None)
+    XDestroyWindow(_blackbox->XDisplay(), geom_window);
+  XDestroyWindow(_blackbox->XDisplay(), empty_window);
+
+  std::for_each(workspacesList.begin(), workspacesList.end(),
+                bt::PointerAssassin());
+
+  delete _rootmenu;
+  delete configmenu;
+
+  delete _workspacemenu;
+  delete _iconmenu;
+
+  delete _windowmenu;
+
+  delete _slit;
+  delete _toolbar;
+
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().supportingWMCheck());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().supported());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().numberOfDesktops());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().desktopGeometry());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().desktopViewport());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().currentDesktop());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().activeWindow());
+  _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                   _blackbox->ewmh().workarea());
+}
+
+
+void BScreen::updateGeomWindow(void) {
+  const WindowStyle &style = _resource.windowStyle();
+  bt::Rect geomr =
+    bt::textRect(screen_info.screenNumber(), style.font,
+                 bt::toUnicode("m:mmmm    m:mmmm"));
+
+  geom_w = geomr.width() + (style.label_margin * 2);
+  geom_h = geomr.height() + (style.label_margin * 2);
+
+  if (geom_window == None) {
+    XSetWindowAttributes setattrib;
+    unsigned long mask = CWColormap | CWSaveUnder;
+    setattrib.colormap = screen_info.colormap();
+    setattrib.save_under = True;
+
+    geom_window =
+      XCreateWindow(_blackbox->XDisplay(), screen_info.rootWindow(),
+                    0, 0, geom_w, geom_h, 0, screen_info.depth(), InputOutput,
+                    screen_info.visual(), mask, &setattrib);
+  }
+
+  const bt::Texture &texture =
+    (style.focus.label.texture() == bt::Texture::Parent_Relative)
+    ? style.focus.title
+    : style.focus.label;
+  geom_w += (texture.borderWidth() * 2);
+  geom_h += (texture.borderWidth() * 2);
+  geom_pixmap = bt::PixmapCache::find(screen_info.screenNumber(),
+                                      texture,
+                                      geom_w,
+                                      geom_h,
+                                      geom_pixmap);
+}
+
+
+void BScreen::reconfigure(void) {
+  LoadStyle();
+
+  updateGeomWindow();
+
+  if (_toolbar) _toolbar->reconfigure();
+  if (_slit) _slit->reconfigure();
+
+  {
+    BlackboxWindowList::iterator it = windowList.begin(),
+                                end = windowList.end();
+    for (; it != end; ++it)
+      if (*it) (*it)->reconfigure();
+  }
+
+  InitMenu();
+
+  configmenu->reconfigure();
+  _rootmenu->reconfigure();
+  _workspacemenu->reconfigure();
+  if (_windowmenu)
+    _windowmenu->reconfigure();
+
+  {
+    WorkspaceList::iterator it = workspacesList.begin(),
+                           end = workspacesList.end();
+    for (; it != end; ++it)
+      (*it)->menu()->reconfigure();
+  }
+}
+
+
+void BScreen::rereadMenu(void) {
+  InitMenu();
+  _rootmenu->reconfigure();
+}
+
+
+void BScreen::LoadStyle(void) {
+  _resource.loadStyle(this, _blackbox->resource().styleFilename());
+
+  if (! _resource.rootCommand().empty())
+    bt::bexec(_resource.rootCommand(), screen_info.displayString());
+}
+
+
+void BScreen::addWorkspace(void) {
+  Workspace *wkspc = new Workspace(this, workspacesList.size());
+  workspacesList.push_back(wkspc);
+  _workspacemenu->insertWorkspace(wkspc);
+
+  _blackbox->ewmh().setNumberOfDesktops(screen_info.rootWindow(),
+                                         workspacesList.size());
+  updateDesktopNamesHint();
+}
+
+
+void BScreen::removeLastWorkspace(void) {
+  if (workspacesList.size() == 1)
+    return;
+
+  Workspace *workspace = workspacesList.back();
+
+  BlackboxWindowList::iterator it = windowList.begin();
+  const BlackboxWindowList::iterator end = windowList.end();
+  for (; it != end; ++it) {
+    BlackboxWindow * const win = *it;
+    if (win->workspace() == workspace->id())
+      win->setWorkspace(workspace->id() - 1);
+  }
+
+  if (current_workspace == workspace->id())
+    setCurrentWorkspace(workspace->id() - 1);
+
+  _workspacemenu->removeWorkspace(workspace->id());
+  workspacesList.pop_back();
+  delete workspace;
+
+  _blackbox->ewmh().setNumberOfDesktops(screen_info.rootWindow(),
+                                         workspacesList.size());
+  updateDesktopNamesHint();
+}
+
+
+void BScreen::setCurrentWorkspace(unsigned int id) {
+  if (id == current_workspace)
+    return;
+
+  assert(id < workspacesList.size());
+
+  _blackbox->XGrabServer();
+
+  // show the empty window... this will prevent unnecessary exposure
+  // of the root window
+  XMapWindow(_blackbox->XDisplay(), empty_window);
+
+  BlackboxWindow * const focused_window = _blackbox->focusedWindow();
+
+  {
+    _workspacemenu->setWorkspaceChecked(current_workspace, false);
+
+    // withdraw windows in reverse order to minimize the number of
+    // Expose events
+    StackingList::const_reverse_iterator it = _stackingList.rbegin();
+    const StackingList::const_reverse_iterator end = _stackingList.rend();
+    for (; it != end; ++it) {
+      BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(*it);
+      if (win && win->workspace() == current_workspace)
+        win->hide();
+    }
+
+    Workspace *workspace = findWorkspace(current_workspace);
+    assert(workspace != 0);
+    if (focused_window && focused_window->workspace() != bt::BSENTINEL) {
+      // remember the window that last had focus
+      workspace->setFocusedWindow(focused_window);
+    } else {
+      workspace->clearFocusedWindow();
+    }
+  }
+
+  current_workspace = id;
+
+  {
+    _workspacemenu->setWorkspaceChecked(current_workspace, true);
+
+    StackingList::const_iterator it = _stackingList.begin();
+    const StackingList::const_iterator end = _stackingList.end();
+    for (; it != end; ++it) {
+      BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(*it);
+      if (win && win->workspace() == current_workspace)
+        win->show();
+    }
+
+    const BlackboxResource &res = _blackbox->resource();
+    if (res.focusLastWindowOnWorkspace()) {
+      Workspace *workspace = findWorkspace(current_workspace);
+      assert(workspace != 0);
+
+      if (workspace->focusedWindow()) {
+        // focus the window that last had focus
+        workspace->focusedWindow()->setInputFocus();
+      } else {
+        // focus the top-most window in the stack
+        for (it = _stackingList.begin(); it != end; ++it) {
+          BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
+          if (!tmp
+              || !tmp->isVisible()
+              || (tmp->workspace() != current_workspace
+                  && tmp->workspace() != bt::BSENTINEL))
+            continue;
+          if (tmp->setInputFocus())
+            break;
+        }
+      }
+    }
+  }
+
+  _blackbox->ewmh().setCurrentDesktop(screen_info.rootWindow(),
+                                      current_workspace);
+
+  XUnmapWindow(_blackbox->XDisplay(), empty_window);
+
+  _blackbox->XUngrabServer();
+}
+
+
+void BScreen::nextWorkspace(void) {
+  if (currentWorkspace() < (workspaceCount() - 1))
+    setCurrentWorkspace(currentWorkspace() + 1);
+  else
+    setCurrentWorkspace(0);
+}
+
+
+void BScreen::prevWorkspace(void) {
+  if (currentWorkspace() > 0)
+    setCurrentWorkspace(currentWorkspace() - 1);
+  else
+    setCurrentWorkspace(workspaceCount() - 1);
+}
+
+
+void BScreen::addWindow(Window w) {
+  manageWindow(w);
+  BlackboxWindow * const win = _blackbox->findWindow(w);
+  if (!win) return;
+
+  updateClientListHint();
+
+  // focus the new window if appropriate
+  switch (win->windowType()) {
+  case WindowTypeDesktop:
+  case WindowTypeDock:
+    // these types should not be focused when managed
+    break;
+
+  default:
+    if (!_blackbox->startingUp() &&
+        (!_blackbox->activeScreen() || _blackbox->activeScreen() == this) &&
+        (win->isTransient() || _blackbox->resource().focusNewWindows())) {
+      XSync(_blackbox->XDisplay(), False); // make sure the frame is mapped..
+      win->setInputFocus();
+      break;
+    }
+  }
+}
+
+
+static StackingList::iterator raiseWindow(StackingList &stackingList,
+                                          StackEntity *entity);
+
+void BScreen::manageWindow(Window w) {
+  XWMHints *wmhints = XGetWMHints(_blackbox->XDisplay(), w);
+  bool slit_client = (wmhints && (wmhints->flags & StateHint) &&
+                      wmhints->initial_state == WithdrawnState);
+  if (wmhints) XFree(wmhints);
+
+  if (slit_client) {
+    if (!_slit) createSlit();
+    _slit->addClient(w);
+    return;
+  }
+
+  (void) new BlackboxWindow(_blackbox, w, this);
+  // verify that we have managed the window
+  BlackboxWindow *win = _blackbox->findWindow(w);
+  if (! win) return;
+
+  if (win->workspace() >= workspaceCount() &&
+      win->workspace() != bt::BSENTINEL)
+    win->setWorkspace(current_workspace);
+
+  Workspace *workspace = findWorkspace(win->workspace());
+  if (!workspace) {
+    win->setWorkspace(bt::BSENTINEL);
+    win->setWindowNumber(bt::BSENTINEL);
+  } else {
+    workspace->addWindow(win);
+  }
+
+  if (!_blackbox->startingUp())
+    placeWindow(win);
+
+  windowList.push_back(win);
+
+  // insert window at the top of the stack
+  (void) _stackingList.insert(win);
+  (void) ::raiseWindow(_stackingList, win);
+  if (!_blackbox->startingUp())
+    restackWindows();
+
+  // 'show' window in the appropriate state
+  switch (_blackbox->startingUp()
+          ? win->currentState()
+          : win->wmHints().initial_state) {
+  case IconicState:
+    win->iconify();
+    break;
+
+  case WithdrawnState:
+    win->hide();
+    break;
+
+  default:
+    win->show();
+    break;
+  } // switch
+}
+
+
+void BScreen::releaseWindow(BlackboxWindow *w) {
+  unmanageWindow(w);
+  updateClientListHint();
+  updateClientListStackingHint();
+}
+
+
+void BScreen::unmanageWindow(BlackboxWindow *win) {
+  win->restore();
+
+  if (win->isIconic()) {
+    _iconmenu->removeItem(win->windowNumber());
+  } else {
+    Workspace *workspace = findWorkspace(win->workspace());
+    if (workspace) {
+      workspace->menu()->removeItem(win->windowNumber());
+      if (workspace->focusedWindow() == win)
+        workspace->clearFocusedWindow();
+    }
+  }
+
+  if (_blackbox->running() && win->isFocused()) {
+    // pass focus to the next appropriate window
+    if (focusFallback(win)) {
+      // focus is going somewhere, but we want to avoid dangling pointers
+      _blackbox->forgetFocusedWindow();
+    } else {
+      // explicitly clear the focus
+      _blackbox->setFocusedWindow(0);
+    }
+  }
+
+  windowList.remove(win);
+  _stackingList.remove(win);
+
+  if (_windowmenu && _windowmenu->window() == win)
+    _windowmenu->hide();
+
+  delete win;
+}
+
+
+bool BScreen::focusFallback(const BlackboxWindow *win) {
+  Workspace *workspace = findWorkspace(win->workspace());
+  if (!workspace)
+    workspace = findWorkspace(current_workspace);
+
+  if (workspace->id() != current_workspace)
+    return false;
+
+  BWindowGroup *group = win->findWindowGroup();
+  if (group) {
+    // focus the top-most window in the group
+    BlackboxWindowList::const_iterator git = group->windows().begin(),
+                                      gend = group->windows().end();
+    StackingList::iterator it = _stackingList.begin(),
+                          end = _stackingList.end();
+    for (; it != end; ++it) {
+      BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
+      if (!tmp
+          || tmp == win
+          || std::find(git, gend, tmp) == gend
+          || !tmp->isVisible()
+          || (tmp->workspace() != current_workspace
+              && tmp->workspace() != bt::BSENTINEL))
+        continue;
+      if (tmp->setInputFocus())
+        return true;
+    }
+  }
+
+  const BlackboxWindow * const zero = 0;
+
+  if (win) {
+    if (win->isTransient()) {
+      BlackboxWindow * const tmp = win->findTransientFor();
+      if (tmp
+          && tmp->isVisible()
+          && (tmp->workspace() == current_workspace
+              || tmp->workspace() == bt::BSENTINEL)
+          && tmp->setInputFocus())
+        return true;
+    }
+
+    // try to focus the top-most window in the same layer as win
+    StackingList::iterator it = _stackingList.layer(win->layer()),
+                          end = std::find(it, _stackingList.end(), zero);
+    assert(it != _stackingList.end() && end != _stackingList.end());
+    for (; it != end; ++it) {
+      BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
+      if (!tmp)
+        break;
+      if (tmp == win
+          || !tmp->isVisible()
+          || (tmp->workspace() != current_workspace
+              && tmp->workspace() != bt::BSENTINEL))
+        continue;
+      if (tmp->setInputFocus())
+        return true;
+    }
+  }
+
+  // focus the top-most window in the stack
+  StackingList::iterator it = _stackingList.begin(),
+                        end = _stackingList.end();
+  for (; it != end; ++it) {
+    BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
+    if (!tmp
+        || tmp == win
+        || !tmp->isVisible()
+        || (tmp->workspace() != current_workspace
+            && tmp->workspace() != bt::BSENTINEL))
+      continue;
+    if (tmp->setInputFocus())
+      return true;
+  }
+
+  return false;
+}
+
+
+/*
+  Raises all windows, preserving the existing stacking order.  Each
+  window is placed at the top of the layer it currently occupies (with
+  transients above).
+*/
+static
+void raiseGroup(StackingList &stackingList, BWindowGroup *group) {
+  BlackboxWindowList windows = group->windows();
+  int layer = StackingList::LayerNormal;
+  for (; layer < StackingList::LayerDesktop; ++layer) {
+    StackingList::iterator
+      it, top = stackingList.layer(StackingList::Layer(layer));
+    const StackingList::iterator begin = stackingList.begin(),
+                                   end = stackingList.end();
+
+    // 'top' points to the top of the layer, we need to start from the
+    // bottom of the layer
+    it = std::find(top, end, (StackEntity *) 0);
+    assert(it != end);
+
+    if (!(*top)) {
+      // nothing in layer
+      break;
+    }
+
+    // walk up the layer, raising all windows in the group
+    for (--it; it != begin; --it) {
+      if (!(*it)) {
+        // top of layer
+        break;
+      }
+
+      BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
+      if (!tmp) {
+        // entity is not a window, or window is not visible on the
+        // current_workspace
+        continue;
+      }
+
+      const BlackboxWindowList::iterator wend = windows.end();
+      BlackboxWindowList::iterator wit;
+      if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
+          continue;
+
+      // found a window in this layer, raise it
+      ++it;
+      stackingList.raise(tmp);
+      // don't bother looking at this window again
+      windows.erase(wit);
+    }
+  }
+}
+
+
+/*
+  Raise all transients, preserving the existing stacking order.
+*/
+static
+void raiseTransients(StackingList::iterator top,
+                     StackingList &stackingList,
+                     BlackboxWindowList &transients) {
+  // 'top' points to the top of the layer, we need to start from the bottom
+  StackingList::iterator begin = stackingList.begin(),
+                           end = stackingList.end(),
+                            it = std::find(top, end, (StackEntity *) 0);
+  assert(it != end);
+  for (--it; it != begin; --it) {
+    if (it == top) {
+      // top of layer
+      break;
+    }
+    BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
+    if (!tmp)
+      continue;
+    BlackboxWindowList::iterator wit = transients.begin(),
+                                wend = transients.end();
+    wit = std::find(wit, wend, tmp);
+    if (wit != wend) {
+      // found a transient in this layer, raise it
+      ++it;
+      stackingList.raise(tmp);
+      // don't bother looking at this window again
+      transients.erase(wit);
+    }
+  }
+}
+
+
+/*
+  Raises the specified stacking entity.  If the entity is a window,
+  all transients are also raised (preserving their stacking order).
+  If the window is part of a group, the entire group is raised (also
+  preserving the stacking order) befor raising the specified window.
+
+  The return value indicates which windows need to be restacked:
+
+  - if raiseWindow() return stackingList.end(), then nothing needs to
+  be restacked.
+
+  - if raiseWindow() returns an iterator for a layer boundary
+  (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
+  be restacked
+
+  - otherwise, the return value points to the bottom most window that
+  needs to be restacked (i.e. the top of the layer down to the return
+  value need to be restacked)
+*/
+static
+StackingList::iterator raiseWindow(StackingList &stackingList,
+                                   StackEntity *entity) {
+  BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
+  if (win) {
+    if (win->isFullScreen() && win->layer() != StackingList::LayerFullScreen) {
+      // move full-screen windows over all other windows when raising
+      win->changeLayer(StackingList::LayerFullScreen);
+      return stackingList.end();
+    }
+  }
+
+  StackingList::iterator layer = stackingList.layer(entity->layer());
+  if (*layer == entity) {
+    // already on top of the layer
+    return stackingList.end();
+  }
+
+  BWindowGroup *group = 0;
+  if (win) {
+    group = win->findWindowGroup();
+    if (group) {
+      // raise all windows in the group before raising 'win'
+      ::raiseGroup(stackingList, group);
+    } else if (win->isTransient()) {
+      // raise non-transient parent before raising transient
+      BlackboxWindow *tmp = win->findNonTransientParent();
+      if (tmp)
+        (void) ::raiseWindow(stackingList, tmp);
+    }
+  }
+
+  // raise the entity
+  StackingList::iterator top = stackingList.raise(entity),
+                         end = stackingList.end();
+  assert(top != end);
+  if (win) {
+    // ... with all transients above it
+    BlackboxWindowList transients = win->buildFullTransientList();
+    if (!transients.empty())
+      raiseTransients(top, stackingList, transients);
+
+    // ... and group transients on top
+    if (group && !win->isGroupTransient()) {
+      const BlackboxWindow * const w = win->isTransient()
+                                       ? win->findNonTransientParent()
+                                       : win;
+      if (!w || !w->isGroupTransient()) {
+        BlackboxWindowList groupTransients = group->transients();
+        BlackboxWindowList::const_iterator wit = groupTransients.begin(),
+                                          wend = groupTransients.end();
+        for (; wit != wend; ++wit) {
+          BlackboxWindowList x = (*wit)->buildFullTransientList();
+          groupTransients.splice(groupTransients.end(), x);
+        }
+        if (!groupTransients.empty())
+          raiseTransients(top, stackingList, groupTransients);
+      }
+    }
+  }
+  if (!group)
+    return top;
+
+  StackingList::iterator it = std::find(top, end, (StackEntity *) 0);
+  assert(it != end);
+  return it;
+}
+
+
+/*
+  Raises the stack entity as above and then updates the stacking on
+  the X server.  The EWMH stacking hint is also updated.
+ */
+void BScreen::raiseWindow(StackEntity *entity) {
+  StackingList::iterator top = ::raiseWindow(_stackingList, entity),
+                         end = _stackingList.end();
+  if (top == end) {
+    // no need to raise entity
+    return;
+  } else if (!(*top)) {
+    // need to restack all windows
+    restackWindows();
+    return;
+  }
+
+  // find the first entity above us (if any)
+  StackEntity *above = 0;
+  StackingList::iterator layer = _stackingList.layer(entity->layer()),
+                         begin = _stackingList.begin(),
+                            it = layer;
+  if (it != begin) {
+    for (--it; it != begin; --it) {
+      if (*it) {
+        above = *it;
+        break;
+      }
+    }
+  }
+
+  // build the stack
+  WindowStack stack;
+  if (above) {
+    // found another entity above the one we are raising
+    stack.push_back(above->windowID());
+  } else {
+    // keep everying under empty_window
+    stack.push_back(empty_window);
+  }
+
+  // go to the layer boundary above 'top'
+  for (it = top; *it && it != begin; --it)
+    ;
+
+  // put all windows from the layer boundary to 'top' into the stack
+  if (!(*it))
+    ++it; // move past layer boundary
+  for (; *it && it != top; ++it) {
+    assert(it != end);
+    stack.push_back((*it)->windowID());
+  }
+  stack.push_back((*top)->windowID());
+
+  XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
+  updateClientListStackingHint();
+}
+
+
+static
+void lowerGroup(StackingList &stackingList, BWindowGroup *group) {
+  BlackboxWindowList windows = group->windows();
+  int layer = StackingList::LayerNormal;
+  for (; layer < StackingList::LayerDesktop; ++layer) {
+    const StackingList::iterator begin = stackingList.begin(),
+                                   end = stackingList.end();
+    StackingList::iterator it = stackingList.layer(StackingList::Layer(layer)),
+                       bottom = std::find(it, end, (StackEntity *) 0);
+    // 'it' points to the top of the layer
+    assert(bottom != end);
+
+    if (!(*it)) {
+      // nothing in layer
+      break;
+    }
+
+    // walk down the layer, lowering all windows in the group
+    for (; it != bottom; ++it) {
+      assert(it != end);
+      BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
+      if (!tmp) {
+        // entity is not a window
+        continue;
+      }
+
+      const BlackboxWindowList::iterator wend = windows.end();
+      BlackboxWindowList::iterator wit;
+      if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
+          continue;
+
+      // found a window in this layer, lower it
+      --it;
+      (void) stackingList.lower(tmp);
+      // don't bother looking at this window again
+      windows.erase(wit);
+    }
+  }
+}
+
+
+static void lowerTransients(StackingList::iterator it,
+                            StackingList &stackingList,
+                            BlackboxWindowList &transients) {
+  // 'it' points to the top of the layer
+  const StackingList::iterator end = stackingList.end(),
+                            bottom = std::find(it, end, (StackEntity *) 0);
+  assert(bottom != end);
+  for (; it != bottom; ++it) {
+    assert(it != end);
+    BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
+    if (!tmp)
+      continue;
+
+    const BlackboxWindowList::iterator wend = transients.end();
+    BlackboxWindowList::iterator wit;
+    if ((wit = std::find(transients.begin(), wend, tmp)) == wend)
+      continue;
+
+    // found a transient in this layer, lower it
+    --it;
+    StackingList::iterator l = stackingList.lower(tmp);
+    // don't bother looking at this window again
+    transients.erase(wit);
+  }
+}
+
+
+/*
+  Lowers the specified stacking entity.  If the entity is a window,
+  all transients are also lowered (preserving their stacking order).
+  If the window is part of a group, the entire group is lowered (also
+  preserving the stacking order) befor lowering the specified window.
+
+  The return value indicates which windows need to be restacked:
+
+  - if lowerWindow() return stackingList.end(), then nothing needs to
+  be restacked.
+
+  - if lowerWindow() returns an iterator for a layer boundary
+  (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
+  be restacked
+
+  - otherwise, the return value points to the top-most window that
+  needs to be restacked (i.e. the return value down to the bottom of
+  the layer need to be restacked)
+*/
+static
+StackingList::iterator lowerWindow(StackingList &stackingList,
+                                   StackEntity *entity,
+                                   bool ignore_group = false) {
+  StackingList::iterator it, end = stackingList.end();
+  BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
+  if (win) {
+    it = end;
+    BWindowGroup *group = win->findWindowGroup();
+    if (!ignore_group && group) {
+      // lower all windows in the group before lowering 'win'
+      ::lowerGroup(stackingList, group);
+      it = std::find(stackingList.begin(), end, (StackEntity *) 0);
+      assert(it != end);
+    }
+
+    const StackingList::iterator layer = stackingList.layer(win->layer());
+    if (win->isTransient()) {
+      BlackboxWindow *const tmp = win->findNonTransientParent();
+      if (tmp && !tmp->isGroupTransient()) {
+        // lower non-transient parent
+        (void) ::lowerWindow(stackingList, tmp, true);
+        if (it == end)
+          it = layer;
+      }
+      return it;
+    }
+
+    // lower transients oinf 'win' and 'win'
+    BlackboxWindowList transients = win->buildFullTransientList();
+    if (!transients.empty()) {
+      ::lowerTransients(layer, stackingList, transients);
+      (void) stackingList.lower(win);
+      if (it == end)
+        it = layer;
+    } else {
+      if (it == end) {
+        it = stackingList.lower(entity);
+        assert(it != end);
+      } else {
+        (void) stackingList.lower(entity);
+      }
+    }
+  } else {
+    // lower the entity
+    it = stackingList.lower(entity);
+    assert(it != end);
+  }
+  return it;
+}
+
+
+void BScreen::lowerWindow(StackEntity *entity) {
+  StackingList::iterator top = ::lowerWindow(_stackingList, entity),
+                         end = _stackingList.end();
+  if (top == end) {
+    // no need to lower entity
+    return;
+  } else if (!(*top)) {
+    // need to restack all windows
+    restackWindows();
+    return;
+  }
+
+  // find the entity above us (if any)
+  StackEntity *above = 0;
+  StackingList::iterator begin = _stackingList.begin(),
+                            it = top;
+  if (it != begin) {
+    for (--it; it != begin; --it) {
+      if (*it) {
+        above = *it;
+        break;
+      }
+    }
+  }
+
+  // build the window stack
+  WindowStack stack;
+  if (above) {
+    // found another entity above the one we are lowering
+    stack.push_back(above->windowID());
+  } else {
+    // keep everying under empty_window
+    stack.push_back(empty_window);
+  }
+
+  // find the layer boundary
+  StackingList::iterator bottom = std::find(top, end, (StackEntity *) 0);
+  assert(bottom != end);
+
+  // put all windows from 'top' to the layer boundary into the stack
+  for (it = top; it != bottom; ++it) {
+    assert(*it && it != end);
+    stack.push_back((*it)->windowID());
+  }
+  stack.push_back((*top)->windowID());
+
+  XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
+  updateClientListStackingHint();
+}
+
+
+void BScreen::restackWindows(void) {
+  WindowStack stack;
+  stack.push_back(empty_window);
+
+  StackingList::const_iterator it, end = _stackingList.end();
+  for (it = _stackingList.begin(); it != end; ++it) {
+    if (*it)
+      stack.push_back((*it)->windowID());
+  }
+
+  XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
+  updateClientListStackingHint();
+}
+
+
+void BScreen::propagateWindowName(const BlackboxWindow * const win) {
+  if (win->isIconic()) {
+    _iconmenu->changeItem(win->windowNumber(),
+                          bt::ellideText(win->iconTitle(), 60,
+                                         bt::toUnicode("...")));
+  } else if (win->workspace() != bt::BSENTINEL) {
+    Workspace *workspace = findWorkspace(win->workspace());
+    assert(workspace != 0);
+    workspace->menu()->changeItem(win->windowNumber(),
+                                  bt::ellideText(win->title(), 60,
+                                                 bt::toUnicode("...")));
+  }
+
+  if (_toolbar && _blackbox->focusedWindow() == win)
+    _toolbar->redrawWindowLabel();
+}
+
+
+void BScreen::nextFocus(void) {
+  BlackboxWindow *focused = _blackbox->focusedWindow(),
+                    *next = 0;
+  BlackboxWindowList::iterator it, end = windowList.end();
+
+  if (focused && focused->workspace() == current_workspace &&
+      focused->screen()->screen_info.screenNumber() ==
+      screen_info.screenNumber()) {
+    it = std::find(windowList.begin(), end, focused);
+    assert(it != end);
+    for (; it != end; ++it) {
+      next = *it;
+      if (next && next != focused && next->workspace() == current_workspace &&
+          next->setInputFocus()) {
+        // we found our new focus target
+        next->setInputFocus();
+        raiseWindow(next);
+        break;
+      }
+      next = 0;
+    }
+  }
+  if (!next) {
+    for (it = windowList.begin(); it != end; ++it) {
+      next = *it;
+      if (next && next->workspace() == current_workspace &&
+          next->setInputFocus()) {
+        // we found our new focus target
+        raiseWindow(next);
+        break;
+      }
+    }
+  }
+}
+
+
+void BScreen::prevFocus(void) {
+  BlackboxWindow *focused = _blackbox->focusedWindow(),
+                    *next = 0;
+  BlackboxWindowList::reverse_iterator it, end = windowList.rend();
+
+  if (focused && focused->workspace() == current_workspace &&
+      focused->screen()->screen_info.screenNumber() ==
+      screen_info.screenNumber()) {
+    it = std::find(windowList.rbegin(), end, focused);
+    assert(it != end);
+    for (; it != end; ++it) {
+      next = *it;
+      if (next && next != focused && next->workspace() == current_workspace &&
+          next->setInputFocus()) {
+        // we found our new focus target
+        next->setInputFocus();
+        raiseWindow(next);
+        break;
+      }
+      next = 0;
+    }
+  }
+  if (!next) {
+    for (it = windowList.rbegin(); it != end; ++it) {
+      next = *it;
+      if (next && next->workspace() == current_workspace &&
+          next->setInputFocus()) {
+        // we found our new focus target
+        raiseWindow(next);
+        break;
+      }
+    }
+  }
+}
+
+
+void BScreen::raiseFocus(void) {
+  BlackboxWindow *focused = _blackbox->focusedWindow();
+  if (! focused || focused->screen() != this)
+    return;
+
+  raiseWindow(focused);
+}
+
+
+void BScreen::InitMenu(void) {
+  if (_rootmenu) {
+    _rootmenu->clear();
+  } else {
+    _rootmenu = new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
+    _rootmenu->showTitle();
+  }
+  bool defaultMenu = True;
+
+  if (_blackbox->resource().menuFilename()) {
+    const char * const filename = _blackbox->resource().menuFilename();
+    bool pipe_menu = filename[0] == '|';
+    FILE *menu_file = pipe_menu
+                      ? popen(filename + 1, "r")
+                      : fopen(filename, "r");
+    if (!menu_file) {
+      perror(_blackbox->resource().menuFilename());
+    } else {
+      if (feof(menu_file)) {
+        fprintf(stderr, "%s: menu file '%s' is empty\n",
+                _blackbox->applicationName().c_str(),
+                _blackbox->resource().menuFilename());
+      } else {
+        char line[1024], label[1024];
+        memset(line, 0, 1024);
+        memset(label, 0, 1024);
+
+        while (fgets(line, 1024, menu_file) && ! feof(menu_file)) {
+          if (line[0] == '#')
+            continue;
+
+          int i, key = 0, index = -1, len = strlen(line);
+
+          for (i = 0; i < len; i++) {
+            if (line[i] == '[') index = 0;
+            else if (line[i] == ']') break;
+            else if (line[i] != ' ')
+              if (index++ >= 0)
+                key += tolower(line[i]);
+          }
+
+          if (key == 517) { // [begin]
+            index = -1;
+            for (i = index; i < len; i++) {
+              if (line[i] == '(')
+                index = 0;
+              else if (line[i] == ')')
+                break;
+              else if (index++ >= 0) {
+                if (line[i] == '\\' && i < len - 1) i++;
+                label[index - 1] = line[i];
+              }
+            }
+
+            if (index == -1) index = 0;
+            label[index] = '\0';
+
+            _rootmenu->setTitle(bt::toUnicode(label));
+            defaultMenu = parseMenuFile(menu_file, _rootmenu);
+            break;
+          }
+        }
+      }
+      if (pipe_menu)
+        pclose(menu_file);
+      else
+        fclose(menu_file);
+    }
+  }
+
+  if (defaultMenu) {
+    _rootmenu->setTitle(bt::toUnicode("_Blackbox"));
+
+    _rootmenu->insertFunction(bt::toUnicode("xterm"),
+                              BScreen::Execute, "xterm");
+    _rootmenu->insertFunction(bt::toUnicode("Restart"),
+                              BScreen::Restart);
+    _rootmenu->insertFunction(bt::toUnicode("Exit"),
+                              BScreen::Exit);
+  } else {
+    _blackbox->saveMenuFilename(_blackbox->resource().menuFilename());
+  }
+}
+
+
+static
+size_t string_within(char begin, char end,
+                     const char *input, size_t start_at, size_t length,
+                     char *output) {
+  bool parse = False;
+  size_t index = 0;
+  size_t i = start_at;
+  for (; i < length; ++i) {
+    if (input[i] == begin) {
+      parse = True;
+    } else if (input[i] == end) {
+      break;
+    } else if (parse) {
+      if (input[i] == '\\' && i < length - 1) i++;
+      output[index++] = input[i];
+    }
+  }
+
+  if (parse)
+    output[index] = '\0';
+  else
+    output[0] = '\0';
+
+  return i;
+}
+
+
+bool BScreen::parseMenuFile(FILE *file, Rootmenu *menu) {
+  char line[1024], keyword[1024], label[1024], command[1024];
+  bool done = False;
+
+  while (! (done || feof(file))) {
+    memset(line, 0, 1024);
+    memset(label, 0, 1024);
+    memset(command, 0, 1024);
+
+    if (! fgets(line, 1024, file))
+      continue;
+
+    if (line[0] == '#') // comment, skip it
+      continue;
+
+    size_t line_length = strlen(line);
+    unsigned int key = 0;
+
+    // get the keyword enclosed in []'s
+    size_t pos = string_within('[', ']', line, 0, line_length, keyword);
+
+    if (keyword[0] == '\0') {  // no keyword, no menu entry
+      continue;
+    } else {
+      size_t len = strlen(keyword);
+      for (size_t i = 0; i < len; ++i) {
+        if (keyword[i] != ' ')
+          key += tolower(keyword[i]);
+      }
+    }
+
+    // get the label enclosed in ()'s
+    pos = string_within('(', ')', line, pos, line_length, label);
+
+    // get the command enclosed in {}'s
+    pos = string_within('{', '}', line, pos, line_length, command);
+
+    switch (key) {
+    case 311: // end
+      done = True;
+
+      break;
+
+    case 328: // sep
+      menu->insertSeparator();
+      break;
+
+    case 333: // nop
+      if (! *label)
+        label[0] = '\0';
+      menu->setItemEnabled(menu->insertItem(bt::toUnicode(label)), false);
+      break;
+
+    case 421: // exec
+      if (! (*label && *command)) {
+        fprintf(stderr,
+                "%s: [exec] error, no menu label and/or command defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      menu->insertFunction(bt::toUnicode(label), BScreen::Execute, command);
+      break;
+
+    case 442: // exit
+      if (! *label) {
+        fprintf(stderr, "%s: [exit] error, no menu label defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      menu->insertFunction(bt::toUnicode(label), BScreen::Exit);
+      break;
+
+    case 561: { // style
+      if (! (*label && *command)) {
+        fprintf(stderr,
+                "%s: [style] error, no menu label and/or filename defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      std::string style = bt::expandTilde(command);
+      menu->insertFunction(bt::toUnicode(label),
+                           BScreen::SetStyle, style.c_str());
+      break;
+    }
+
+    case 630: // config
+      if (! *label) {
+        fprintf(stderr, "%s: [config] error, no label defined",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      menu->insertItem(bt::toUnicode(label), configmenu);
+      break;
+
+    case 740: { // include
+      if (! *label) {
+        fprintf(stderr, "%s: [include] error, no filename defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      bool pipe_menu = label[0] == '|';
+      std::string newfile = bt::expandTilde(pipe_menu ? label + 1 : label);
+      FILE *submenufile = pipe_menu
+                          ? popen(newfile.c_str(), "r")
+                          : fopen(newfile.c_str(), "r");
+
+      if (! submenufile) {
+        perror(newfile.c_str());
+        continue;
+      }
+
+      struct stat buf;
+      if (!pipe_menu
+          && (fstat(fileno(submenufile), &buf) || ! S_ISREG(buf.st_mode))) {
+        fprintf(stderr, "%s: [include] error: '%s' is not a regular file\n",
+                _blackbox->applicationName().c_str(), newfile.c_str());
+        fclose(submenufile);
+        break;
+      }
+
+      if (! feof(submenufile)) {
+        if (! parseMenuFile(submenufile, menu))
+          _blackbox->saveMenuFilename(newfile);
+
+        if (pipe_menu)
+          pclose(submenufile);
+        else
+          fclose(submenufile);
+      }
+    }
+
+      break;
+
+    case 767: { // submenu
+      if (! *label) {
+        fprintf(stderr, "%s: [submenu] error, no menu label defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      Rootmenu *submenu =
+        new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
+      submenu->showTitle();
+
+      if (*command)
+        submenu->setTitle(bt::toUnicode(command));
+      else
+        submenu->setTitle(bt::toUnicode(label));
+
+      parseMenuFile(file, submenu);
+      menu->insertItem(bt::toUnicode(label), submenu);
+    }
+
+      break;
+
+    case 773: { // restart
+      if (! *label) {
+        fprintf(stderr, "%s: [restart] error, no menu label defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      if (*command) {
+        menu->insertFunction(bt::toUnicode(label),
+                             BScreen::RestartOther, command);
+      } else {
+        menu->insertFunction(bt::toUnicode(label), BScreen::Restart);
+      }
+      break;
+    }
+
+    case 845: { // reconfig
+      if (! *label) {
+        fprintf(stderr, "%s: [reconfig] error, no menu label defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      menu->insertFunction(bt::toUnicode(label), BScreen::Reconfigure);
+      break;
+    }
+
+    case 995:    // stylesdir
+    case 1113: { // stylesmenu
+      bool newmenu = ((key == 1113) ? True : False);
+
+      if (! *label || (! *command && newmenu)) {
+        fprintf(stderr,
+                "%s: [stylesdir/stylesmenu] error, no directory defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      char *directory = ((newmenu) ? command : label);
+
+      std::string stylesdir = bt::expandTilde(directory);
+
+      struct stat statbuf;
+
+      if (stat(stylesdir.c_str(), &statbuf) == -1) {
+        fprintf(stderr,
+                "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
+                _blackbox->applicationName().c_str(), stylesdir.c_str());
+        continue;
+      }
+      if (! S_ISDIR(statbuf.st_mode)) {
+        fprintf(stderr,
+                "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
+                _blackbox->applicationName().c_str(), stylesdir.c_str());
+        continue;
+      }
+
+      Rootmenu *stylesmenu;
+
+      if (newmenu) {
+        stylesmenu =
+          new Rootmenu(*_blackbox, screen_info.screenNumber(),this);
+        stylesmenu->showTitle();
+      } else {
+        stylesmenu = menu;
+      }
+
+      DIR *d = opendir(stylesdir.c_str());
+      struct dirent *p;
+      std::vector<std::string> ls;
+
+      while((p = readdir(d)))
+        ls.push_back(p->d_name);
+
+      closedir(d);
+
+      std::sort(ls.begin(), ls.end());
+
+      std::vector<std::string>::iterator it = ls.begin(),
+                                        end = ls.end();
+      for (; it != end; ++it) {
+        std::string& fname = *it;
+
+        if (fname[0] == '.' || fname[fname.size()-1] == '~')
+          continue;
+
+        std::string style = stylesdir;
+        style += '/';
+        style += fname;
+
+        if (! stat(style.c_str(), &statbuf) && S_ISREG(statbuf.st_mode)) {
+          // convert 'This_Long_Name' to 'This Long Name'
+          std::replace(fname.begin(), fname.end(), '_', ' ');
+
+          stylesmenu->insertFunction(bt::toUnicode(fname),
+                                     BScreen::SetStyle, style);
+        }
+      }
+
+      if (newmenu) {
+        stylesmenu->setTitle(bt::toUnicode(label));
+        menu->insertItem(bt::toUnicode(label), stylesmenu);
+      }
+
+      _blackbox->saveMenuFilename(stylesdir);
+    }
+      break;
+
+    case 1090: { // workspaces
+      if (! *label) {
+        fprintf(stderr, "%s: [workspaces] error, no menu label defined\n",
+                _blackbox->applicationName().c_str());
+        continue;
+      }
+
+      menu->insertItem(bt::toUnicode(label), _workspacemenu);
+      break;
+    }
+    } // switch
+  }
+
+  return (menu->count() == 0);
+}
+
+
+void BScreen::shutdown(void) {
+  XSelectInput(_blackbox->XDisplay(), screen_info.rootWindow(),
+               NoEventMask);
+  XSync(_blackbox->XDisplay(), False);
+
+  // unmanage all windows, but keep them in the current stacking order
+  WindowStack stack;
+  StackingList::const_iterator it, end = _stackingList.end();
+  for (it = _stackingList.begin(); it != end; ++it) {
+    if (!(*it))
+      continue;
+    const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
+    if (!win)
+      continue;
+    stack.push_back(win->clientWindow());
+  }
+
+  while(! windowList.empty())
+    unmanageWindow(windowList.back());
+
+  if (!stack.empty())
+    XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
+
+  if (_slit)
+    _slit->shutdown();
+}
+
+
+void BScreen::showGeometry(GeometryType type, const bt::Rect &rect) {
+  if (! geom_visible) {
+    XMoveResizeWindow(_blackbox->XDisplay(), geom_window,
+                      (screen_info.width() - geom_w) / 2,
+                      (screen_info.height() - geom_h) / 2, geom_w, geom_h);
+    XMapWindow(_blackbox->XDisplay(), geom_window);
+    XRaiseWindow(_blackbox->XDisplay(), geom_window);
+
+    geom_visible = True;
+  }
+
+  char label[80];
+  switch (type) {
+  case Position:
+    sprintf(label, "X:%4d    Y:%4d", rect.x(), rect.y());
+    break;
+  case Size:
+    sprintf(label, "W:%4u    H:%4u", rect.width(), rect.height());
+    break;
+  default:
+    assert(0);
+  }
+
+  const WindowStyle &style = _resource.windowStyle();
+  const bt::Texture &texture =
+    (style.focus.label.texture() == bt::Texture::Parent_Relative)
+    ? style.focus.title
+    : style.focus.label;
+  const bt::Rect u(0, 0, geom_w, geom_h);
+  const bt::Rect t(style.label_margin,
+                   style.label_margin,
+                   geom_w - (style.label_margin * 2),
+                   geom_h - (style.label_margin * 2));
+  const bt::Pen pen(screen_info.screenNumber(), style.focus.text);
+  bt::drawTexture(screen_info.screenNumber(), texture, geom_window,
+                  u, u, geom_pixmap);
+  bt::drawText(style.font, pen, geom_window, t, style.alignment,
+               bt::toUnicode(label));
+}
+
+
+void BScreen::hideGeometry(void) {
+  if (geom_visible) {
+    XUnmapWindow(_blackbox->XDisplay(), geom_window);
+    geom_visible = False;
+  }
+}
+
+
+void BScreen::addStrut(bt::EWMH::Strut *strut) {
+  strutList.push_back(strut);
+  updateAvailableArea();
+}
+
+
+void BScreen::removeStrut(bt::EWMH::Strut *strut) {
+  strutList.remove(strut);
+  updateAvailableArea();
+}
+
+
+void BScreen::updateStrut(void) {
+  updateAvailableArea();
+}
+
+
+const bt::Rect& BScreen::availableArea(void) {
+  if (_blackbox->resource().fullMaximization())
+    return screen_info.rect(); // return the full screen
+  return usableArea;
+}
+
+
+void BScreen::updateAvailableArea(void) {
+  bt::Rect new_area;
+
+  /* these values represent offsets from the screen edge
+   * we look for the biggest offset on each edge and then apply them
+   * all at once
+   * do not be confused by the similarity to the names of Rect's members
+   */
+  bt::EWMH::Strut current;
+
+  StrutList::const_iterator sit = strutList.begin(), send = strutList.end();
+
+  for(; sit != send; ++sit) {
+    const bt::EWMH::Strut* const strut = *sit;
+    if (strut->left > current.left)
+      current.left = strut->left;
+    if (strut->top > current.top)
+      current.top = strut->top;
+    if (strut->right > current.right)
+      current.right = strut->right;
+    if (strut->bottom > current.bottom)
+      current.bottom = strut->bottom;
+  }
+
+  new_area.setPos(current.left, current.top);
+  new_area.setSize(screen_info.width() - (current.left + current.right),
+                   screen_info.height() - (current.top + current.bottom));
+
+  if (new_area != usableArea) {
+    usableArea = new_area;
+    BlackboxWindowList::iterator wit = windowList.begin(),
+                                wend = windowList.end();
+    for (; wit != wend; ++wit)
+      if ((*wit)->isMaximized()) (*wit)->remaximize();
+
+    updateWorkareaHint();
+  }
+}
+
+
+Workspace* BScreen::findWorkspace(unsigned int index) const {
+  if (index == bt::BSENTINEL)
+    return 0;
+  assert(index < workspacesList.size());
+  return workspacesList[index];
+}
+
+
+void BScreen::clientMessageEvent(const XClientMessageEvent * const event) {
+  if (event->format != 32) return;
+
+  if (event->message_type == _blackbox->ewmh().numberOfDesktops()) {
+    unsigned int number = event->data.l[0];
+    if (number > workspaceCount()) {
+      for (; number != workspaceCount(); --number)
+        addWorkspace();
+    } else if (number < workspaceCount()) {
+      for (; number != workspaceCount(); ++number)
+        removeLastWorkspace();
+    }
+  } else if (event->message_type == _blackbox->ewmh().desktopNames()) {
+    readDesktopNames();
+  } else if (event->message_type == _blackbox->ewmh().currentDesktop()) {
+    const unsigned int workspace = event->data.l[0];
+    if (workspace < workspaceCount() && workspace != current_workspace)
+      setCurrentWorkspace(workspace);
+  }
+}
+
+
+void BScreen::buttonPressEvent(const XButtonEvent * const event) {
+  /*
+    set this screen active.  keygrabs and input focus will stay on
+    this screen until the user focuses a window on another screen or
+    makes another screen active.
+  */
+  _blackbox->setActiveScreen(this);
+
+  if (event->button == 2) {
+    _workspacemenu->popup(event->x_root, event->y_root);
+  } else if (event->button == 3) {
+    _blackbox->checkMenu();
+    _rootmenu->popup(event->x_root, event->y_root);
+  } else if (event->button == 4 &&
+             _blackbox->resource().changeWorkspaceWithMouseWheel()) {
+    nextWorkspace();
+  } else if (event->button == 5 &&
+             _blackbox->resource().changeWorkspaceWithMouseWheel()) {
+    prevWorkspace();
+  }
+}
+
+
+void BScreen::propertyNotifyEvent(const XPropertyEvent * const event) {
+  if (event->atom == _blackbox->ewmh().activeWindow() && _toolbar)
+    _toolbar->redrawWindowLabel();
+}
+
+
+void BScreen::unmapNotifyEvent(const XUnmapEvent * const event) {
+  // handle synthetic unmap events according to ICCCM section 4.1.4
+  BlackboxWindow *win = _blackbox->findWindow(event->window);
+  if (win && event->event == screen_info.rootWindow()
+      && !event->from_configure)
+    win->unmapNotifyEvent(event);
+}
+
+
+void BScreen::toggleFocusModel(FocusModel model) {
+  std::for_each(windowList.begin(), windowList.end(),
+                std::mem_fun(&BlackboxWindow::ungrabButtons));
+
+  _blackbox->resource().setFocusModel(model);
+
+  std::for_each(windowList.begin(), windowList.end(),
+                std::mem_fun(&BlackboxWindow::grabButtons));
+}
+
+
+void BScreen::updateWorkareaHint(void) const {
+  unsigned long *workarea, *tmp;
+
+  tmp = workarea = new unsigned long[workspaceCount() * 4];
+
+  for (unsigned int i = 0; i < workspaceCount(); ++i) {
+    tmp[0] = usableArea.x();
+    tmp[1] = usableArea.y();
+    tmp[2] = usableArea.width();
+    tmp[3] = usableArea.height();
+    tmp += 4;
+  }
+
+  _blackbox->ewmh().setWorkarea(screen_info.rootWindow(),
+                                 workarea, workspaceCount());
+
+  delete [] workarea;
+}
+
+
+void BScreen::updateDesktopNamesHint(void) const {
+  std::vector<bt::ustring> names(workspacesList.size());
+  WorkspaceList::const_iterator it = workspacesList.begin();
+  const WorkspaceList::const_iterator end = workspacesList.end();
+  for (; it != end; ++it)
+    names.push_back((*it)->name());
+  _blackbox->ewmh().setDesktopNames(screen_info.rootWindow(), names);
+}
+
+
+void BScreen::updateClientListHint(void) const {
+  if (windowList.empty()) {
+    _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                      _blackbox->ewmh().clientList());
+    return;
+  }
+
+  bt::EWMH::WindowList clientList(windowList.size());
+
+  std::transform(windowList.begin(), windowList.end(), clientList.begin(),
+                 std::mem_fun(&BlackboxWindow::clientWindow));
+
+  _blackbox->ewmh().setClientList(screen_info.rootWindow(), clientList);
+}
+
+
+void BScreen::updateClientListStackingHint(void) const {
+  bt::EWMH::WindowList stack;
+
+  // we store windows in top-to-bottom order, but the EWMH wants
+  // bottom-to-top...
+  StackingList::const_reverse_iterator it = _stackingList.rbegin(),
+                                      end = _stackingList.rend();
+  for (; it != end; ++it) {
+    const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
+    if (win) stack.push_back(win->clientWindow());
+  }
+
+  if (stack.empty()) {
+    _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
+                                     _blackbox->ewmh().clientListStacking());
+    return;
+  }
+
+  _blackbox->ewmh().setClientListStacking(screen_info.rootWindow(), stack);
+}
+
+
+void BScreen::readDesktopNames(void) {
+  std::vector<bt::ustring> names;
+  if(! _blackbox->ewmh().readDesktopNames(screen_info.rootWindow(), names))
+    return;
+
+  std::vector<bt::ustring>::const_iterator it = names.begin();
+  const std::vector<bt::ustring>::const_iterator end = names.end();
+  WorkspaceList::iterator wit = workspacesList.begin();
+  const WorkspaceList::iterator wend = workspacesList.end();
+
+  for (; wit != wend && it != end; ++wit, ++it) {
+    if ((*wit)->name() != *it)
+      (*wit)->setName(*it);
+  }
+
+  if (names.size() < workspacesList.size())
+    updateDesktopNamesHint();
+}
+
+
+BlackboxWindow *BScreen::window(unsigned int workspace, unsigned int id) {
+  StackingList::const_iterator it = _stackingList.begin(),
+                              end = _stackingList.end();
+  for (; it != end; ++it) {
+    BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
+    if (win && win->workspace() == workspace && win->windowNumber() == id)
+      return win;
+  }
+  assert(false); // should not happen
+  return 0;
+}
+
+
+void BScreen::placeWindow(BlackboxWindow *win) {
+  bt::Rect r = win->frameRect();
+
+  // if the client/user has explicitly placed the window, honor it
+  if (win->wmNormalHints().flags & (PPosition | USPosition)) {
+    // make sure that the window is not placed outside of the workarea
+    win->configure(r.inside(usableArea));
+    return;
+  }
+
+  switch (win->windowType()) {
+  case WindowTypeDesktop:
+    win->configure(screen_info.rect());
+    return;
+  case WindowTypeDialog: {
+    BlackboxWindow *w = win->findTransientFor();
+    bt::Rect p = w ? w->frameRect() : usableArea;
+    const int x = static_cast<int>(p.x() + (p.width() - r.width()) / 2);
+    const int y = static_cast<int>(p.y() + (p.height() - r.height()) / 2);
+    r.setPos(x, y);
+    break;
+  }
+  default: {
+    bool placed = false;
+    BlackboxResource &res = _blackbox->resource();
+    switch (res.windowPlacementPolicy()) {
+    case RowSmartPlacement:
+    case ColSmartPlacement:
+      placed = smartPlacement(win->workspace(), r, usableArea);
+      break;
+    case CenterPlacement:
+      placed = centerPlacement(r, usableArea);
+      break;
+    default:
+      break; // handled below
+    } // switch
+    if (!placed) {
+      cascadePlacement(r, usableArea);
+      cascade_x += _resource.windowStyle().title_height;
+      cascade_y += _resource.windowStyle().title_height;
+    }
+    break;
+  }
+  } // switch
+
+  win->configure(r.inside(usableArea));
+}
+
+
+bool BScreen::cascadePlacement(bt::Rect &win,
+                               const bt::Rect &avail) {
+  if (cascade_x > (avail.width() / 2) ||
+      cascade_y > (avail.height() / 2))
+    cascade_x = cascade_y = 32;
+
+  if (cascade_x == 32) {
+    cascade_x += avail.x();
+    cascade_y += avail.y();
+  }
+
+  win.setPos(cascade_x, cascade_y);
+
+  if (win.right()  > avail.right() ||
+      win.bottom() > avail.bottom()) {
+    cascade_x = cascade_y = 32;
+
+    cascade_x += avail.x();
+    cascade_y += avail.y();
+
+    win.setPos(cascade_x, cascade_y);
+  }
+
+  return True;
+}
+
+
+bool BScreen::centerPlacement(bt::Rect &rect, const bt::Rect &avail)
+{
+  const int x =
+    static_cast<int>(avail.x() + (avail.width() - rect.width()) / 2);
+  const int y =
+    static_cast<int>(avail.y() + (avail.height() - rect.height()) / 2);
+  rect.setPos(x, y);
+  return true;
+}
+
+
+bool BScreen::smartPlacement(unsigned int workspace, bt::Rect& rect,
+                             const bt::Rect& avail) {
+  // constants
+  const BlackboxResource &res = _blackbox->resource();
+  const bool row_placement =
+    (res.windowPlacementPolicy() == RowSmartPlacement);
+  const bool leftright =
+    (res.rowPlacementDirection() == LeftRight);
+  const bool topbottom =
+    (res.colPlacementDirection() == TopBottom);
+  const bool ignore_shaded = res.placementIgnoresShaded();
+
+  const int left_border   = leftright ? 0 : -1;
+  const int top_border    = topbottom ? 0 : -1;
+  const int right_border  = leftright ? 1 : 0;
+  const int bottom_border = topbottom ? 1 : 0;
+
+  StackingList::const_iterator w_it, w_end;
+
+  /*
+    build a sorted vector of x and y grid boundaries
+
+    note: we use one vector to reduce the number of allocations
+    std::vector must do.. we allocate as much memory as we would need
+    in the worst case scenario and work with that
+  */
+  std::vector<int> coords(_stackingList.size() * 4 + 4);
+  std::vector<int>::iterator
+    x_begin = coords.begin(),
+    x_end   = x_begin,
+    y_begin = coords.begin() + _stackingList.size() * 2 + 2,
+    y_end   = y_begin;
+
+  {
+    std::vector<int>::iterator x_it = x_begin, y_it = y_begin;
+
+    *x_it++ = avail.left();
+    *x_it++ = avail.right();
+    x_end += 2;
+
+    *y_it++ = avail.top();
+    *y_it++ = avail.bottom();
+    y_end += 2;
+
+
+    for (w_it  = _stackingList.begin(), w_end = _stackingList.end();
+         w_it != w_end; ++w_it) {
+      const BlackboxWindow * const win =
+        dynamic_cast<const BlackboxWindow *>(*w_it);
+      if (!win) continue;
+
+      if (win->windowType() == WindowTypeDesktop)
+        continue;
+      if (win->isIconic())
+        continue;
+      if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
+        continue;
+      if (ignore_shaded && win->isShaded())
+        continue;
+
+      *x_it++ = std::max(win->frameRect().left() + left_border,
+                         avail.left());
+      *x_it++ = std::min(win->frameRect().right() + right_border,
+                         avail.right());
+      x_end += 2;
+
+      *y_it++ = std::max(win->frameRect().top() + top_border,
+                         avail.top());
+      *y_it++ = std::min(win->frameRect().bottom() + bottom_border,
+                         avail.bottom());
+      y_end += 2;
+    }
+
+    assert(x_end <= y_begin);
+  }
+
+  std::sort(x_begin, x_end);
+  x_end = std::unique(x_begin, x_end);
+
+  std::sort(y_begin, y_end);
+  y_end = std::unique(y_begin, y_end);
+
+  // build a distribution grid
+  unsigned int gw = x_end - x_begin - 1,
+               gh = y_end - y_begin - 1;
+  std::vector<bool> used_grid(gw * gh);
+  std::fill_n(used_grid.begin(), used_grid.size(), false);
+
+  for (w_it = _stackingList.begin(), w_end = _stackingList.end();
+       w_it != w_end; ++w_it) {
+    const BlackboxWindow * const win =
+      dynamic_cast<const BlackboxWindow *>(*w_it);
+    if (!win) continue;
+
+    if (win->windowType() == WindowTypeDesktop)
+      continue;
+    if (win->isIconic())
+      continue;
+    if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
+      continue;
+    if (ignore_shaded && win->isShaded())
+      continue;
+
+    const int w_left =
+      std::max(win->frameRect().left() + left_border,
+               avail.left());
+    const int w_top =
+      std::max(win->frameRect().top() + top_border,
+               avail.top());
+    const int w_right =
+      std::min(win->frameRect().right() + right_border,
+               avail.right());
+    const int w_bottom =
+      std::min(win->frameRect().bottom() + bottom_border,
+               avail.bottom());
+
+    // which areas of the grid are used by this window?
+    std::vector<int>::iterator l_it = std::find(x_begin, x_end, w_left),
+                               r_it = std::find(x_begin, x_end, w_right),
+                               t_it = std::find(y_begin, y_end, w_top),
+                               b_it = std::find(y_begin, y_end, w_bottom);
+    assert(l_it != x_end &&
+           r_it != x_end &&
+           t_it != y_end &&
+           b_it != y_end);
+
+    const unsigned int left   = l_it - x_begin,
+                       right  = r_it - x_begin,
+                       top    = t_it - y_begin,
+                       bottom = b_it - y_begin;
+
+    for (unsigned int gy = top; gy < bottom; ++gy)
+      for (unsigned int gx = left; gx < right; ++gx)
+        used_grid[(gy * gw) + gx] = true;
+  }
+
+  /*
+    Attempt to fit the window into any of the empty areas in the grid.
+    The exact order is dependent upon the users configuration (as
+    shown below).
+
+    row placement:
+    - outer -> vertical axis
+    - inner -> horizontal axis
+
+    col placement:
+    - outer -> horizontal axis
+    - inner -> vertical axis
+  */
+
+  int gx, gy;
+  int &outer = row_placement ? gy : gx;
+  int &inner = row_placement ? gx : gy;
+  const int outer_delta = row_placement
+                          ? (topbottom ? 1 : -1)
+                          : (leftright ? 1 : -1);
+  const int inner_delta = row_placement
+                          ? (leftright ? 1 : -1)
+                          : (topbottom ? 1 : -1);
+  const int outer_begin = row_placement
+                          ? (topbottom ? 0 : static_cast<int>(gh) - 1)
+                          : (leftright ? 0 : static_cast<int>(gw) - 1);
+  const int outer_end   = row_placement
+                          ? (topbottom ? static_cast<int>(gh) : -1)
+                          : (leftright ? static_cast<int>(gw) : -1);
+  const int inner_begin = row_placement
+                          ? (leftright ? 0 : static_cast<int>(gw) - 1)
+                          : (topbottom ? 0 : static_cast<int>(gh) - 1);
+  const int inner_end   = row_placement
+                          ? (leftright ? static_cast<int>(gw) : -1)
+                          : (topbottom ? static_cast<int>(gh) : -1);
+
+  bt::Rect where;
+  bool fit = false;
+  for (outer = outer_begin; ! fit && outer != outer_end;
+       outer += outer_delta) {
+    for (inner = inner_begin; ! fit && inner != inner_end;
+         inner += inner_delta) {
+      // see if the window fits in a single unused area
+      if (used_grid[(gy * gw) + gx]) continue;
+
+      where.setCoords(*(x_begin + gx), *(y_begin + gy),
+                      *(x_begin + gx + 1), *(y_begin + gy + 1));
+
+      if (where.width()  >= rect.width() &&
+          where.height() >= rect.height()) {
+        fit = true;
+        break;
+      }
+
+      /*
+        see if we neighboring spaces are unused
+
+        TODO: we should grid fit in the same direction as above,
+        instead of always right->left and top->bottom
+      */
+      int gx2 = gx, gy2 = gy;
+
+      if (rect.width() > where.width()) {
+        for (gx2 = gx+1; gx2 < static_cast<int>(gw); ++gx2) {
+          if (used_grid[(gy * gw) + gx2]) {
+            --gx2;
+            break;
+          }
+
+          where.setCoords(*(x_begin + gx), *(y_begin + gy),
+                          *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
+
+          if (where.width() >= rect.width()) break;
+        }
+
+        if (gx2 >= static_cast<int>(gw)) --gx2;
+      }
+
+      if (rect.height() > where.height()) {
+        for (gy2 = gy; gy2 < static_cast<int>(gh); ++gy2) {
+          if (used_grid[(gy2 * gw) + gx]) {
+            --gy2;
+            break;
+          }
+
+          where.setCoords(*(x_begin + gx), *(y_begin + gy),
+                          *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
+
+          if (where.height() >= rect.height()) break;
+        }
+
+        if (gy2 >= static_cast<int>(gh)) --gy2;
+      }
+
+      if (where.width()  >= rect.width() &&
+          where.height() >= rect.height()) {
+        fit = true;
+
+        // make sure all spaces are really available
+        for (int gy3 = gy; gy3 <= gy2; ++gy3) {
+          for (int gx3 = gx; gx3 <= gx2; ++gx3) {
+            if (used_grid[(gy3 * gw) + gx3]) {
+              fit = false;
+              break;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (! fit) {
+    const int screen_area = avail.width() * avail.height();
+    const int window_area = rect.width() * rect.height();
+    if (window_area > screen_area / 8) {
+      // center windows that don't fit (except for small windows)
+      return centerPlacement(rect, avail);
+    }
+    return false;
+  }
+
+  // adjust the location() based on left/right and top/bottom placement
+  if (! leftright)
+    where.setX(where.right() - rect.width() + 1);
+  if (! topbottom)
+    where.setY(where.bottom() - rect.height() + 1);
+
+  rect.setPos(where.x(), where.y());
+
+  return true;
+}
+
+
+void BScreen::createSlit(void) {
+  assert(_slit == 0);
+
+  _slit = new Slit(this);
+  _stackingList.insert(_slit);
+  restackWindows();
+}
+
+
+void BScreen::destroySlit(void) {
+  assert(_slit != 0);
+
+  _stackingList.remove(_slit);
+  delete _slit;
+  _slit = 0;
+}
+
+
+void BScreen::createToolbar(void) {
+  assert(_toolbar == 0);
+
+  _toolbar = new Toolbar(this);
+  _stackingList.insert(_toolbar);
+  restackWindows();
+}
+
+
+void BScreen::destroyToolbar(void) {
+  assert(_toolbar != 0);
+
+  _stackingList.remove(_toolbar);
+  delete _toolbar;
+  _toolbar = 0;
+}
+
+
+Windowmenu *BScreen::windowmenu(BlackboxWindow *win) {
+  if (!_windowmenu)
+    _windowmenu = new Windowmenu(*_blackbox, screen_info.screenNumber());
+  _windowmenu->setWindow(win);
+  return _windowmenu;
+}
+
+
+void BScreen::addIcon(BlackboxWindow *win) {
+  if (win->workspace() != bt::BSENTINEL) {
+    Workspace *workspace = findWorkspace(win->workspace());
+    assert(workspace != 0);
+    workspace->removeWindow(win);
+  }
+
+  if (win->isTransient()) {
+    BlackboxWindow * const tmp = win->findNonTransientParent();
+    if (tmp) {
+      win->setWindowNumber(bt::BSENTINEL);
+      return;
+    }
+  }
+
+  const bt::ustring s =
+    bt::ellideText(win->iconTitle(), 60, bt::toUnicode("..."));
+  int id = _iconmenu->insertItem(s);
+  _blackbox->ewmh().setWMVisibleIconName(win->clientWindow(), s);
+  win->setWindowNumber(id);
+}
+
+
+void BScreen::removeIcon(BlackboxWindow *win) {
+  if (win->windowNumber() != bt::BSENTINEL) {
+    _iconmenu->removeItem(win->windowNumber());
+    _blackbox->ewmh().removeProperty(win->clientWindow(),
+                                     _blackbox->ewmh().wmVisibleIconName());
+  }
+
+  Workspace *workspace = findWorkspace(current_workspace);
+  assert(workspace != 0);
+  workspace->addWindow(win);
+}
+
+
+BlackboxWindow *BScreen::icon(unsigned int id) {
+  StackingList::const_iterator it = _stackingList.begin(),
+                              end = _stackingList.end();
+  for (; it != end; ++it) {
+    BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
+    if (win && win->isIconic() && win->windowNumber() == id)
+      return win;
+  }
+  assert(false); // should not happen
+  return 0;
+}
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/src/ScreenResource.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/ScreenResource.cc
new file mode 100644
index 0000000..239ddc6
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/ScreenResource.cc
@@ -0,0 +1,553 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// ScreenResource.ccfor Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "ScreenResource.hh"
+
+#include "Screen.hh"
+#include "Slit.hh"
+#include "Toolbar.hh"
+
+#include <Menu.hh>
+#include <Resource.hh>
+
+#include <assert.h>
+
+
+static const int iconify_width  = 9;
+static const int iconify_height = 9;
+static const unsigned char iconify_bits[] =
+  { 0x00, 0x00, 0x82, 0x00, 0xc6, 0x00, 0x6c, 0x00, 0x38,
+    0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01 };
+
+static const int maximize_width  = 9;
+static const int maximize_height = 9;
+static const unsigned char maximize_bits[] =
+  { 0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01 };
+
+static const int restore_width  = 9;
+static const int restore_height = 9;
+static const unsigned char restore_bits[] =
+  { 0xf8, 0x01, 0xf8, 0x01, 0x08, 0x01, 0x3f, 0x01, 0x3f,
+    0x01, 0xe1, 0x01, 0x21, 0x00, 0x21, 0x00, 0x3f, 0x00 };
+
+static const int close_width  = 9;
+static const int close_height = 9;
+static const unsigned char close_bits[] =
+  { 0x83, 0x01, 0xc7, 0x01, 0xee, 0x00, 0x7c, 0x00, 0x38,
+    0x00, 0x7c, 0x00, 0xee, 0x00, 0xc7, 0x01, 0x83, 0x01 };
+
+
+void ScreenResource::save(bt::Resource& res, BScreen* screen) {
+  char rc_string[128];
+  char *placement = (char *) 0;
+  unsigned int number = screen->screenNumber();
+
+  switch (_slitOptions.placement) {
+  case Slit::TopLeft: placement = "TopLeft"; break;
+  case Slit::CenterLeft: placement = "CenterLeft"; break;
+  case Slit::BottomLeft: placement = "BottomLeft"; break;
+  case Slit::TopCenter: placement = "TopCenter"; break;
+  case Slit::BottomCenter: placement = "BottomCenter"; break;
+  case Slit::TopRight: placement = "TopRight"; break;
+  case Slit::BottomRight: placement = "BottomRight"; break;
+  case Slit::CenterRight: default: placement = "CenterRight"; break;
+  }
+
+  sprintf(rc_string, "session.screen%u.slit.placement", number);
+  res.write(rc_string, placement);
+
+  sprintf(rc_string, "session.screen%u.slit.direction", number);
+  res.write(rc_string, (_slitOptions.direction == Slit::Horizontal) ?
+            "Horizontal" : "Vertical");
+
+  sprintf(rc_string, "session.screen%u.slit.onTop", number);
+  res.write(rc_string, _slitOptions.always_on_top);
+
+  sprintf(rc_string, "session.screen%u.slit.autoHide", number);
+  res.write(rc_string, _slitOptions.auto_hide);
+
+
+  sprintf(rc_string,  "session.screen%u.enableToolbar", number);
+  res.write(rc_string, _toolbarOptions.enabled);
+
+  sprintf(rc_string, "session.screen%u.toolbar.onTop", number);
+  res.write(rc_string, _toolbarOptions.always_on_top);
+
+  sprintf(rc_string, "session.screen%u.toolbar.autoHide", number);
+  res.write(rc_string, _toolbarOptions.auto_hide);
+
+  switch (_toolbarOptions.placement) {
+  case Toolbar::TopLeft: placement = "TopLeft"; break;
+  case Toolbar::BottomLeft: placement = "BottomLeft"; break;
+  case Toolbar::TopCenter: placement = "TopCenter"; break;
+  case Toolbar::TopRight: placement = "TopRight"; break;
+  case Toolbar::BottomRight: placement = "BottomRight"; break;
+  case Toolbar::BottomCenter: default: placement = "BottomCenter"; break;
+  }
+
+  sprintf(rc_string, "session.screen%u.toolbar.placement", number);
+  res.write(rc_string, placement);
+
+  sprintf(rc_string, "session.screen%u.workspaces", number);
+  res.write(rc_string, workspace_count);
+
+  std::vector<bt::ustring>::const_iterator it = workspace_names.begin(),
+                                          end = workspace_names.end();
+  bt::ustring save_string = *it++;
+  for (; it != end; ++it) {
+    save_string += ',';
+    save_string += *it;
+  }
+
+  sprintf(rc_string, "session.screen%u.workspaceNames", number);
+  res.write(rc_string, bt::toLocale(save_string).c_str());
+
+  // these options can not be modified at runtime currently
+
+  sprintf(rc_string, "session.screen%u.toolbar.widthPercent", number);
+  res.write(rc_string, _toolbarOptions.width_percent);
+
+  sprintf(rc_string, "session.screen%u.strftimeFormat", number);
+  res.write(rc_string, _toolbarOptions.strftime_format.c_str());
+}
+
+void ScreenResource::load(bt::Resource& res, unsigned int screen) {
+  char name_lookup[128], class_lookup[128];
+  std::string str;
+
+  // toolbar settings
+  sprintf(name_lookup,  "session.screen%u.enableToolbar", screen);
+  sprintf(class_lookup, "Session.screen%u.enableToolbar", screen);
+  _toolbarOptions.enabled = res.read(name_lookup, class_lookup, true);
+
+  sprintf(name_lookup,  "session.screen%u.toolbar.widthPercent", screen);
+  sprintf(class_lookup, "Session.screen%u.Toolbar.WidthPercent", screen);
+  _toolbarOptions.width_percent = res.read(name_lookup, class_lookup, 66);
+
+  sprintf(name_lookup, "session.screen%u.toolbar.placement", screen);
+  sprintf(class_lookup, "Session.screen%u.Toolbar.Placement", screen);
+  str = res.read(name_lookup, class_lookup, "BottomCenter");
+  if (! strcasecmp(str.c_str(), "TopLeft"))
+    _toolbarOptions.placement = Toolbar::TopLeft;
+  else if (! strcasecmp(str.c_str(), "BottomLeft"))
+    _toolbarOptions.placement = Toolbar::BottomLeft;
+  else if (! strcasecmp(str.c_str(), "TopCenter"))
+    _toolbarOptions.placement = Toolbar::TopCenter;
+  else if (! strcasecmp(str.c_str(), "TopRight"))
+    _toolbarOptions.placement = Toolbar::TopRight;
+  else if (! strcasecmp(str.c_str(), "BottomRight"))
+    _toolbarOptions.placement = Toolbar::BottomRight;
+  else
+    _toolbarOptions.placement = Toolbar::BottomCenter;
+
+  sprintf(name_lookup,  "session.screen%u.toolbar.onTop", screen);
+  sprintf(class_lookup, "Session.screen%u.Toolbar.OnTop", screen);
+  _toolbarOptions.always_on_top = res.read(name_lookup, class_lookup, false);
+
+  sprintf(name_lookup,  "session.screen%u.toolbar.autoHide", screen);
+  sprintf(class_lookup, "Session.screen%u.Toolbar.autoHide", screen);
+  _toolbarOptions.auto_hide = res.read(name_lookup, class_lookup, false);
+
+  sprintf(name_lookup,  "session.screen%u.strftimeFormat", screen);
+  sprintf(class_lookup, "Session.screen%u.StrftimeFormat", screen);
+  _toolbarOptions.strftime_format =
+    res.read(name_lookup, class_lookup, "%I:%M %p");
+
+  // slit settings
+  sprintf(name_lookup, "session.screen%u.slit.placement", screen);
+  sprintf(class_lookup, "Session.screen%u.Slit.Placement", screen);
+  str = res.read(name_lookup, class_lookup, "CenterRight");
+  if (! strcasecmp(str.c_str(), "TopLeft"))
+    _slitOptions.placement = Slit::TopLeft;
+  else if (! strcasecmp(str.c_str(), "CenterLeft"))
+    _slitOptions.placement = Slit::CenterLeft;
+  else if (! strcasecmp(str.c_str(), "BottomLeft"))
+    _slitOptions.placement = Slit::BottomLeft;
+  else if (! strcasecmp(str.c_str(), "TopCenter"))
+    _slitOptions.placement = Slit::TopCenter;
+  else if (! strcasecmp(str.c_str(), "BottomCenter"))
+    _slitOptions.placement = Slit::BottomCenter;
+  else if (! strcasecmp(str.c_str(), "TopRight"))
+    _slitOptions.placement = Slit::TopRight;
+  else if (! strcasecmp(str.c_str(), "BottomRight"))
+    _slitOptions.placement = Slit::BottomRight;
+  else
+    _slitOptions.placement = Slit::CenterRight;
+
+  sprintf(name_lookup, "session.screen%u.slit.direction", screen);
+  sprintf(class_lookup, "Session.screen%u.Slit.Direction", screen);
+  str = res.read(name_lookup, class_lookup, "Vertical");
+  if (! strcasecmp(str.c_str(), "Horizontal"))
+    _slitOptions.direction = Slit::Horizontal;
+  else
+    _slitOptions.direction = Slit::Vertical;
+
+  sprintf(name_lookup, "session.screen%u.slit.onTop", screen);
+  sprintf(class_lookup, "Session.screen%u.Slit.OnTop", screen);
+  _slitOptions.always_on_top = res.read(name_lookup, class_lookup, false);
+
+  sprintf(name_lookup, "session.screen%u.slit.autoHide", screen);
+  sprintf(class_lookup, "Session.screen%u.Slit.AutoHide", screen);
+  _slitOptions.auto_hide = res.read(name_lookup, class_lookup, false);
+
+  // general screen settings
+
+  sprintf(name_lookup,  "session.screen%u.workspaces", screen);
+  sprintf(class_lookup, "Session.screen%u.Workspaces", screen);
+  workspace_count = res.read(name_lookup, class_lookup, 4);
+
+  if (! workspace_names.empty())
+    workspace_names.clear();
+
+  sprintf(name_lookup,  "session.screen%u.workspaceNames", screen);
+  sprintf(class_lookup, "Session.screen%u.WorkspaceNames", screen);
+  bt::ustring ustr = bt::toUnicode(res.read(name_lookup, class_lookup));
+  if (!ustr.empty()) {
+    bt::ustring::const_iterator it = ustr.begin();
+    const bt::ustring::const_iterator end = ustr.end();
+    for (;;) {
+      const bt::ustring::const_iterator i =
+        std::find(it, end, static_cast<bt::ustring::value_type>(','));
+      workspace_names.push_back(bt::ustring(it, i));
+      it = i;
+      if (it == end)
+        break;
+      ++it;
+    }
+  }
+}
+
+
+void ScreenResource::loadStyle(BScreen* screen, const std::string& style) {
+  const bt::Display& display = screen->blackbox()->display();
+  unsigned int screen_num = screen->screenNumber();
+
+  // use the user selected style
+  bt::Resource res(style);
+  if (!res.valid())
+    res.load(DEFAULTSTYLE);
+
+  // load menu style
+  bt::MenuStyle::get(*screen->blackbox(), screen_num)->load(res);
+
+  // load window style
+  _windowStyle.font.setFontName(res.read("window.font", "Window.Font"));
+
+  _windowStyle.iconify.load(screen_num, iconify_bits,
+                            iconify_width, iconify_height);
+  _windowStyle.maximize.load(screen_num, maximize_bits,
+                             maximize_width, maximize_height);
+  _windowStyle.restore.load(screen_num, restore_bits,
+                            restore_width, restore_height);
+  _windowStyle.close.load(screen_num, close_bits,
+                          close_width, close_height);
+
+  // focused window style
+  _windowStyle.focus.text =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("window.label.focus.textColor",
+                                   "Window.Label.Focus.TextColor",
+                                   "black"));
+  _windowStyle.focus.foreground =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("window.button.focus.foregroundColor",
+                                   "Window.Button.Focus.ForegroundColor",
+                                   res.read("window.button.focus.picColor",
+                                            "Window.Button.Focus.PicColor",
+                                            "black")));
+  _windowStyle.focus.title =
+    bt::textureResource(display, screen_num, res,
+                        "window.title.focus",
+                        "Window.Title.Focus",
+                        "white");
+  _windowStyle.focus.label =
+    bt::textureResource(display, screen_num, res,
+                        "window.label.focus",
+                        "Window.Label.Focus",
+                        "white");
+  _windowStyle.focus.button =
+    bt::textureResource(display, screen_num, res,
+                        "window.button.focus",
+                        "Window.Button.Focus",
+                        "white");
+  _windowStyle.focus.handle =
+    bt::textureResource(display, screen_num, res,
+                        "window.handle.focus",
+                        "Window.Handle.Focus",
+                        "white");
+  _windowStyle.focus.grip =
+    bt::textureResource(display, screen_num, res,
+                        "window.grip.focus",
+                        "Window.Grip.Focus",
+                        "white");
+  _windowStyle.focus.frame_border =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("window.frame.focus.borderColor",
+                                   "Window.Frame.Focus.BorderColor",
+                                   "white"));
+
+  // unfocused window style
+  _windowStyle.unfocus.text =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("window.label.unfocus.textColor",
+                                   "Window.Label.Unfocus.TextColor",
+                                   "white"));
+  _windowStyle.unfocus.foreground =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("window.button.unfocus.foregroundColor",
+                                   "Window.Button.Unfocus.ForegroundColor",
+                                   res.read("window.button.unfocus.picColor",
+                                            "Window.Button.Unfocus.PicColor",
+                                            "white")));
+  _windowStyle.unfocus.title =
+    bt::textureResource(display, screen_num, res,
+                        "window.title.unfocus",
+                        "Window.Title.Unfocus",
+                        "black");
+  _windowStyle.unfocus.label =
+    bt::textureResource(display, screen_num, res,
+                        "window.label.unfocus",
+                        "Window.Label.Unfocus",
+                        "black");
+  _windowStyle.unfocus.button =
+    bt::textureResource(display, screen_num, res,
+                        "window.button.unfocus",
+                        "Window.Button.Unfocus",
+                        "black");
+  _windowStyle.unfocus.handle =
+    bt::textureResource(display, screen_num, res,
+                        "window.handle.unfocus",
+                        "Window.Handle.Unfocus",
+                        "black");
+  _windowStyle.unfocus.grip =
+    bt::textureResource(display, screen_num, res,
+                        "window.grip.unfocus",
+                        "Window.Grip.Unfocus",
+                        "black");
+  _windowStyle.unfocus.frame_border =
+    bt::Color::namedColor(display, screen_num,
+			  res.read("window.frame.unfocus.borderColor",
+                                   "Window.Frame.Unfocus.BorderColor",
+                                   "black"));
+
+  _windowStyle.pressed =
+    bt::textureResource(display, screen_num, res,
+                        "window.button.pressed",
+                        "Window.Button.Pressed",
+                        "black");
+
+  _windowStyle.alignment =
+    bt::alignResource(res, "window.alignment", "Window.Alignment");
+
+  _windowStyle.title_margin =
+    res.read("window.title.marginWidth", "Window.Title.MarginWidth", 2);
+  _windowStyle.label_margin =
+    res.read("window.label.marginWidth", "Window.Label.MarginWidth", 2);
+  _windowStyle.button_margin =
+    res.read("window.button.marginWidth", "Window.Button.MarginWidth", 2);
+  _windowStyle.frame_border_width =
+    res.read("window.frame.borderWidth", "Window.Frame.BorderWidth", 1);
+  _windowStyle.handle_height =
+    res.read("window.handleHeight", "Window.HandleHeight", 6);
+
+  // the height of the titlebar is based upon the height of the font being
+  // used to display the window's title
+  _windowStyle.button_width =
+    std::max(std::max(std::max(std::max(_windowStyle.iconify.width(),
+                                        _windowStyle.iconify.height()),
+                               std::max(_windowStyle.maximize.width(),
+                                        _windowStyle.maximize.height())),
+                      std::max(_windowStyle.restore.width(),
+                               _windowStyle.restore.height())),
+             std::max(_windowStyle.close.width(),
+                      _windowStyle.close.height())) +
+    ((std::max(_windowStyle.focus.button.borderWidth(),
+               _windowStyle.unfocus.button.borderWidth()) +
+      _windowStyle.button_margin) * 2);
+  _windowStyle.label_height =
+    std::max(bt::textHeight(screen_num, _windowStyle.font) +
+             ((std::max(_windowStyle.focus.label.borderWidth(),
+                        _windowStyle.unfocus.label.borderWidth()) +
+               _windowStyle.label_margin) * 2),
+             _windowStyle.button_width);
+  _windowStyle.button_width = std::max(_windowStyle.button_width,
+                                       _windowStyle.label_height);
+  _windowStyle.title_height =
+    _windowStyle.label_height +
+    ((std::max(_windowStyle.focus.title.borderWidth(),
+               _windowStyle.unfocus.title.borderWidth()) +
+      _windowStyle.title_margin) * 2);
+  _windowStyle.grip_width = (_windowStyle.button_width * 2);
+  _windowStyle.handle_height +=
+    (std::max(_windowStyle.focus.handle.borderWidth(),
+              _windowStyle.unfocus.handle.borderWidth()) * 2);
+
+  // load toolbar style
+  _toolbarStyle.font.setFontName(res.read("toolbar.font", "Toolbar.Font"));
+
+  _toolbarStyle.toolbar =
+    bt::textureResource(display, screen_num, res,
+                        "toolbar",
+                        "Toolbar",
+                        "white");
+  _toolbarStyle.slabel =
+    bt::textureResource(display, screen_num, res,
+                        "toolbar.label",
+                        "Toolbar.Label",
+                        "white");
+  _toolbarStyle.wlabel =
+    bt::textureResource(display, screen_num, res,
+                        "toolbar.windowLabel",
+                        "Toolbar.Label",
+                        "white");
+  _toolbarStyle.button =
+    bt::textureResource(display, screen_num, res,
+                        "toolbar.button",
+                        "Toolbar.Button",
+                        "white");
+  _toolbarStyle.pressed =
+    bt::textureResource(display, screen_num, res,
+                        "toolbar.button.pressed",
+                        "Toolbar.Button.Pressed",
+                        "black");
+
+  _toolbarStyle.clock =
+    bt::textureResource(display, screen_num, res,
+                        "toolbar.clock",
+                        "Toolbar.Label",
+                        "white");
+
+  _toolbarStyle.slabel_text =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("toolbar.label.textColor",
+                                   "Toolbar.Label.TextColor",
+                                   "black"));
+  _toolbarStyle.wlabel_text =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("toolbar.windowLabel.textColor",
+                                   "Toolbar.Label.TextColor",
+                                   "black"));
+  _toolbarStyle.clock_text =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("toolbar.clock.textColor",
+                                   "Toolbar.Label.TextColor",
+                                   "black"));
+  _toolbarStyle.foreground =
+    bt::Color::namedColor(display, screen_num,
+                          res.read("toolbar.button.foregroundColor",
+                                   "Toolbar.Button.ForegroundColor",
+                                   res.read("toolbar.button.picColor",
+                                            "Toolbar.Button.PicColor",
+                                            "black")));
+  _toolbarStyle.alignment =
+    bt::alignResource(res, "toolbar.alignment", "Toolbar.Alignment");
+
+  _toolbarStyle.frame_margin =
+    res.read("toolbar.marginWidth", "Toolbar.MarginWidth", 2);
+  _toolbarStyle.label_margin =
+    res.read("toolbar.label.marginWidth", "Toolbar.Label.MarginWidth", 2);
+  _toolbarStyle.button_margin =
+    res.read("toolbar.button.marginWidth", "Toolbar.Button.MarginWidth", 2);
+
+  const bt::Bitmap &left = bt::Bitmap::leftArrow(screen_num),
+                  &right = bt::Bitmap::rightArrow(screen_num);
+  _toolbarStyle.button_width =
+    std::max(std::max(left.width(), left.height()),
+             std::max(right.width(), right.height()))
+    + ((_toolbarStyle.button.borderWidth() + _toolbarStyle.button_margin) * 2);
+  _toolbarStyle.label_height =
+    std::max(bt::textHeight(screen_num, _toolbarStyle.font)
+             + ((std::max(std::max(_toolbarStyle.slabel.borderWidth(),
+                                   _toolbarStyle.wlabel.borderWidth()),
+                          _toolbarStyle.clock.borderWidth())
+                 + _toolbarStyle.label_margin) * 2),
+             _toolbarStyle.button_width);
+  _toolbarStyle.button_width = std::max(_toolbarStyle.button_width,
+                                        _toolbarStyle.label_height);
+  _toolbarStyle.toolbar_height = _toolbarStyle.label_height
+                                 + ((_toolbarStyle.toolbar.borderWidth()
+                                     + _toolbarStyle.frame_margin) * 2);
+  _toolbarStyle.hidden_height =
+    std::max(_toolbarStyle.toolbar.borderWidth()
+             + _toolbarStyle.frame_margin, 1u);
+
+  // load slit style
+  _slitStyle.slit = bt::textureResource(display,
+                                        screen_num,
+                                        res,
+                                        "slit",
+                                        "Slit",
+                                        _toolbarStyle.toolbar);
+  _slitStyle.margin = res.read("slit.marginWidth", "Slit.MarginWidth", 2);
+
+  const std::string rc_file = screen->blackbox()->resource().rcFilename();
+  root_command =
+    bt::Resource(rc_file).read("rootCommand",
+                               "RootCommand",
+                               res.read("rootCommand",
+                                        "RootCommand"));
+
+  // sanity checks
+  bt::Texture flat_black;
+  flat_black.setDescription("flat solid");
+  flat_black.setColor1(bt::Color(0, 0, 0));
+
+  if (_windowStyle.focus.title.texture() == bt::Texture::Parent_Relative)
+    _windowStyle.focus.title = flat_black;
+  if (_windowStyle.unfocus.title.texture() == bt::Texture::Parent_Relative)
+    _windowStyle.unfocus.title = flat_black;
+  if (_windowStyle.focus.handle.texture() == bt::Texture::Parent_Relative)
+    _windowStyle.focus.handle = flat_black;
+  if (_windowStyle.unfocus.handle.texture() == bt::Texture::Parent_Relative)
+    _windowStyle.unfocus.handle = flat_black;
+  if (_windowStyle.focus.grip.texture() == bt::Texture::Parent_Relative)
+    _windowStyle.focus.grip = flat_black;
+  if (_windowStyle.unfocus.grip.texture() == bt::Texture::Parent_Relative)
+    _windowStyle.unfocus.grip = flat_black;
+
+  if (_toolbarStyle.toolbar.texture() == bt::Texture::Parent_Relative)
+    _toolbarStyle.toolbar = flat_black;
+
+  if (_slitStyle.slit.texture() == bt::Texture::Parent_Relative)
+    _slitStyle.slit = flat_black;
+}
+
+const bt::ustring ScreenResource::workspaceName(unsigned int i) const {
+  // handle both requests for new workspaces beyond what we started with
+  // and for those that lack a name
+  if (i > workspace_count || i >= workspace_names.size())
+    return bt::ustring();
+  return workspace_names[i];
+}
+
+void ScreenResource::setWorkspaceName(unsigned int i,
+                                      const bt::ustring &name) {
+    if (i >= workspace_names.size()) {
+        workspace_names.reserve(i + 1);
+        workspace_names.insert(workspace_names.begin() + i, name);
+    } else {
+        workspace_names[i] = name;
+    }
+}
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/src/main.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/main.cc
new file mode 100644
index 0000000..2c48e0d
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/src/main.cc
@@ -0,0 +1,149 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// main.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+// #define PRINT_SIZES
+
+#if defined(PRINT_SIZES)
+#  include "Screen.hh"
+#  include "Slit.hh"
+#  include "Toolbar.hh"
+#  include "Window.hh"
+#endif
+
+#include "blackbox.hh"
+#include "../version.h"
+
+#include <stdio.h>
+
+
+static void showHelp(int exitval) {
+  // print version - this should not be localized!
+  printf("Blackbox %s\n"
+         "Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry\n"
+         "Copyright (c) 1997 - 2000, 2002 - 2005 Bradley T Hughes\n",
+         __blackbox_version);
+
+  // print program usage and command line options
+  printf("  -display <string>\t\tuse display connection.\n"
+         "  -single <string>\t\tmanage the default screen only\n"
+         "  -rc <string>\t\t\tuse alternate resource file.\n"
+         "  -version\t\t\tdisplay version and exit.\n"
+         "  -help\t\t\t\tdisplay this help text and exit.\n\n");
+
+  // some people have requested that we print out compile options
+  // as well
+  printf("Compile time options:\n"
+         "  Debugging:\t\t\t%s\n"
+         "  Shape:\t\t\t%s\n\n",
+#ifdef    DEBUG
+         "yes",
+#else // !DEBUG
+         "no",
+#endif // DEBUG
+
+#ifdef    SHAPE
+         "yes"
+#else // !SHAPE
+         "no"
+#endif // SHAPE
+         );
+
+  ::exit(exitval);
+}
+
+int main(int argc, char **argv) {
+  const char *dpy_name = 0;
+  std::string rc_file;
+  bool multi_head = true;
+
+  for (int i = 1; i < argc; ++i) {
+    if (! strcmp(argv[i], "-help")) {
+      showHelp(0);
+    } else if (! strcmp(argv[i], "-version")) {
+      // print current version string, this should not be localized!
+      printf("Blackbox %s\n"
+             "Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry\n"
+             "Copyright (c) 1997 - 2000, 2002 - 2005 Bradley T Hughes\n",
+             __blackbox_version);
+
+      ::exit(0);
+    } else if (! strcmp(argv[i], "-rc")) {
+      // look for alternative rc file to use
+
+      if ((++i) >= argc) {
+        fprintf(stderr, "error: '-rc' requires and argument\n");
+        ::exit(1);
+      }
+
+      rc_file = argv[i];
+    } else if (! strcmp(argv[i], "-display")) {
+      // check for -display option... to run on a display other than the one
+      // set by the environment variable DISPLAY
+
+      if ((++i) >= argc) {
+        fprintf(stderr, "error: '-display' requires an argument\n");
+        ::exit(1);
+      }
+
+      dpy_name = argv[i];
+      std::string dtmp = "DISPLAY=";
+      dtmp += dpy_name;
+
+      if (putenv(const_cast<char*>(dtmp.c_str()))) {
+        fprintf(stderr,
+                "warning: couldn't set environment variable 'DISPLAY'\n");
+        perror("putenv()");
+      }
+    } else if (! strcmp(argv[i], "-single")) {
+      multi_head = false;
+    } else { // invalid command line option
+      showHelp(-1);
+    }
+  }
+
+#ifdef    __EMX__
+  _chdir2(getenv("X11ROOT"));
+#endif // __EMX__
+
+  if (rc_file.empty())
+    rc_file = "~/.blackboxrc";
+  rc_file = bt::expandTilde(rc_file);
+
+#if defined(PRINT_SIZES)
+  printf("Blackbox      : %4d bytes\n"
+         "BScreen       : %4d bytes\n"
+         "BlackboxWindow: %4d bytes\n"
+         "Toolbar       : %4d bytes\n"
+         "Slit          : %4d bytes\n",
+         sizeof(Blackbox),
+         sizeof(BScreen),
+         sizeof(BlackboxWindow),
+         sizeof(Toolbar),
+         sizeof(Slit));
+#endif
+
+  Blackbox blackbox(argv, dpy_name, rc_file, multi_head);
+  blackbox.run();
+  return 0;
+}
diff --git a/.pc/020_fix-ftbfs-gcc-4.3.patch/util/bsetroot.cc b/.pc/020_fix-ftbfs-gcc-4.3.patch/util/bsetroot.cc
new file mode 100644
index 0000000..9fe3c5d
--- /dev/null
+++ b/.pc/020_fix-ftbfs-gcc-4.3.patch/util/bsetroot.cc
@@ -0,0 +1,364 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// bsetroot - a background setting utility
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh at debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "bsetroot.hh"
+
+#include <Pen.hh>
+#include <Texture.hh>
+
+#include <cctype>
+
+#include <X11/Xatom.h>
+#include <stdio.h>
+
+
+// ignore all X errors
+static int x11_error(::Display *, XErrorEvent *)
+{ return 0; }
+
+
+bsetroot::bsetroot(int argc, char **argv, char *dpy_name,
+                   bool multi_head): display(dpy_name, multi_head) {
+  XSetErrorHandler(x11_error); // silently handle all errors
+
+  bool mod = False, sol = False, grd = False;
+  int mod_x = 0, mod_y = 0;
+
+  for (int i = 1; i < argc; i++) {
+    if (! strcmp("-help", argv[i])) {
+      usage();
+    } else if ((! strcmp("-fg", argv[i])) ||
+               (! strcmp("-foreground", argv[i])) ||
+               (! strcmp("-from", argv[i]))) {
+      if ((++i) >= argc) usage(1);
+
+      fore = argv[i];
+    } else if ((! strcmp("-bg", argv[i])) ||
+               (! strcmp("-background", argv[i])) ||
+               (! strcmp("-to", argv[i]))) {
+      if ((++i) >= argc) usage(1);
+
+      back = argv[i];
+    } else if (! strcmp("-solid", argv[i])) {
+      if ((++i) >= argc) usage(1);
+
+      fore = argv[i];
+      sol = True;
+    } else if (! strcmp("-mod", argv[i])) {
+      if ((++i) >= argc) usage();
+
+      mod_x = atoi(argv[i]);
+
+      if ((++i) >= argc) usage();
+
+      mod_y = atoi(argv[i]);
+
+      if (mod_x < 1) mod_x = 1;
+      if (mod_y < 1) mod_y = 1;
+
+      mod = True;
+    } else if (! strcmp("-gradient", argv[i])) {
+      if ((++i) >= argc) usage();
+
+      grad = argv[i];
+      grd = True;
+    } else if (! strcmp("-display", argv[i])) {
+      // -display passed through tests ealier... we
+      i++;
+    } else {
+      usage();
+    }
+  }
+
+  if ((mod + sol + grd) != True) {
+    fprintf(stderr,
+            "bsetroot: error: must specify one of: -solid, -mod, -gradient\n");
+
+    usage(2);
+  }
+
+  // keep the server grabbed while rendering all screens
+  XGrabServer(display.XDisplay());
+
+  if (sol && ! fore.empty())
+    solid();
+  else if (mod && mod_x && mod_y && ! (fore.empty() || back.empty()))
+    modula(mod_x, mod_y);
+  else if (grd && ! (grad.empty() || fore.empty() || back.empty()))
+    gradient();
+  else
+    usage();
+
+  // ungrab the server and discard any events
+  XUngrabServer(display.XDisplay());
+  XSync(display.XDisplay(), True);
+}
+
+
+bsetroot::~bsetroot(void) {
+  XSetCloseDownMode(display.XDisplay(), RetainPermanent);
+  XKillClient(display.XDisplay(), AllTemporary);
+}
+
+
+// adapted from wmsetbg
+void bsetroot::setPixmapProperty(int screen, Pixmap pixmap) {
+  Atom rootpmap_id = XInternAtom(display.XDisplay(), "_XROOTPMAP_ID", False),
+       esetroot_id = XInternAtom(display.XDisplay(), "ESETROOT_PMAP_ID", False);
+
+  const bt::ScreenInfo &screen_info = display.screenInfo(screen);
+
+  Atom type;
+  int format;
+  unsigned long length, after;
+  unsigned char *data = 0;
+  Pixmap xrootpmap = None;
+  Pixmap esetrootpmap = None;
+
+  // Clear out the old _XROOTPMAP_ID property
+  XGetWindowProperty(display.XDisplay(), screen_info.rootWindow(),
+		     rootpmap_id, 0L, 1L, True, AnyPropertyType,
+                     &type, &format, &length, &after, &data);
+
+  if (data && type == XA_PIXMAP && format == 32 && length == 1)
+    xrootpmap = *(reinterpret_cast<Pixmap *>(data));
+
+  if (data) XFree(data);
+
+  // Clear out the old ESETROOT_PMAP_ID property
+  XGetWindowProperty(display.XDisplay(), screen_info.rootWindow(),
+                     esetroot_id, 0L, 1L, True, AnyPropertyType,
+                     &type, &format, &length, &after, &data);
+
+  if (data && type == XA_PIXMAP && format == 32 && length == 1)
+    esetrootpmap = *(reinterpret_cast<Pixmap *>(data));
+
+  if (data) XFree(data);
+
+  // Destroy the old pixmaps
+  if (xrootpmap)
+    XKillClient(display.XDisplay(), xrootpmap);
+  if (esetrootpmap && esetrootpmap != xrootpmap)
+    XKillClient(display.XDisplay(), esetrootpmap);
+
+  if (pixmap) {
+    XChangeProperty(display.XDisplay(), screen_info.rootWindow(),
+		    rootpmap_id, XA_PIXMAP, 32, PropModeReplace,
+		    reinterpret_cast<unsigned char *>(&pixmap), 1);
+    XChangeProperty(display.XDisplay(), screen_info.rootWindow(),
+		    esetroot_id, XA_PIXMAP, 32, PropModeReplace,
+		    reinterpret_cast<unsigned char *>(&pixmap), 1);
+  }
+}
+
+
+unsigned long bsetroot::duplicateColor(unsigned int screen,
+                                       const bt::Color &color) {
+  /*
+    When using a colormap that is not read-only, we need to
+    reallocate the color we are using.  The application's color cache
+    will be freed on exit, and another client can allocate a new
+    color at the same pixel value, which will immediately change the
+    color of the root window.
+
+    this is not what we want, so we need to make sure that the pixel
+    we are using is doubly allocated, so that it stays around with the
+    pixmap.  It will be released when we use
+    XKillClient(..., AllTemporary);
+  */
+  const bt::ScreenInfo &screen_info = display.screenInfo(screen);
+  unsigned long pixel = color.pixel(screen);
+  XColor xcolor;
+  xcolor.pixel = pixel;
+  XQueryColor(display.XDisplay(), screen_info.colormap(), &xcolor);
+  if (! XAllocColor(display.XDisplay(), screen_info.colormap(),
+                    &xcolor)) {
+    fprintf(stderr, "warning: couldn't duplicate color %02x/%02x/%02x\n",
+            color.red(), color.green(), color.blue());
+  }
+  return pixel;
+}
+
+
+void bsetroot::solid(void) {
+  bt::Color c = bt::Color::namedColor(display, 0, fore);
+
+  for (unsigned int screen = 0; screen < display.screenCount(); screen++) {
+    const bt::ScreenInfo &screen_info = display.screenInfo(screen);
+    unsigned long pixel = duplicateColor(screen, c);
+
+    XSetWindowBackground(display.XDisplay(), screen_info.rootWindow(), pixel);
+    XClearWindow(display.XDisplay(), screen_info.rootWindow());
+
+    Pixmap pixmap =
+      XCreatePixmap(display.XDisplay(), screen_info.rootWindow(),
+                    8, 8, DefaultDepth(display.XDisplay(), screen));
+
+    bt::Pen pen(screen, c);
+    XFillRectangle(display.XDisplay(), pixmap, pen.gc(), 0, 0, 8, 8);
+
+    setPixmapProperty(screen, pixmap);
+  }
+}
+
+
+void bsetroot::modula(int x, int y) {
+  char data[32];
+  long pattern;
+
+  unsigned int screen, i;
+
+  bt::Color f = bt::Color::namedColor(display, 0, fore),
+            b = bt::Color::namedColor(display, 0, back);
+
+  for (pattern = 0, screen = 0; screen < display.screenCount(); screen++) {
+    for (i = 0; i < 16; i++) {
+      pattern <<= 1;
+      if ((i % x) == 0)
+        pattern |= 0x0001;
+    }
+
+    for (i = 0; i < 16; i++) {
+      if ((i %  y) == 0) {
+        data[(i * 2)] = static_cast<char>(0xff);
+        data[(i * 2) + 1] = static_cast<char>(0xff);
+      } else {
+        data[(i * 2)] = pattern & 0xff;
+        data[(i * 2) + 1] = (pattern >> 8) & 0xff;
+      }
+    }
+
+    const bt::ScreenInfo &screen_info = display.screenInfo(screen);
+
+    XGCValues gcv;
+    gcv.foreground = duplicateColor(screen, f);
+    gcv.background = duplicateColor(screen, b);
+
+    GC gc = XCreateGC(display.XDisplay(), screen_info.rootWindow(),
+                      GCForeground | GCBackground, &gcv);
+
+    Pixmap pixmap = XCreatePixmap(display.XDisplay(),
+				  screen_info.rootWindow(),
+				  16, 16, screen_info.depth());
+
+    Pixmap bitmap =
+      XCreateBitmapFromData(display.XDisplay(), screen_info.rootWindow(),
+                            data, 16, 16);
+    XCopyPlane(display.XDisplay(), bitmap, pixmap, gc,
+               0, 0, 16, 16, 0, 0, 1l);
+    XFreeGC(display.XDisplay(), gc);
+    XFreePixmap(display.XDisplay(), bitmap);
+
+    XSetWindowBackgroundPixmap(display.XDisplay(),
+                               screen_info.rootWindow(),
+                               pixmap);
+    XClearWindow(display.XDisplay(), screen_info.rootWindow());
+
+    setPixmapProperty(screen, pixmap);
+  }
+}
+
+
+void bsetroot::gradient(void) {
+  /*
+    we have to be sure that neither raised nor sunken is specified otherwise
+    odd looking borders appear.  So we convert to lowercase then look for
+    'raised' or 'sunken' in the description and erase them.  To be paranoid
+    the search is done in a loop.
+  */
+  std::string descr = bt::tolower(grad);
+  std::string::size_type pos;
+  while ((pos = descr.find("raised")) != std::string::npos)
+    descr.erase(pos, 6); // 6 is strlen raised
+  while ((pos = descr.find("sunken")) != std::string::npos)
+    descr.erase(pos, 6);
+
+  // now add on 'flat' to prevent the bevels from being added
+  descr += "flat";
+
+  bt::Color f = bt::Color::namedColor(display, 0, fore);
+  bt::Color b = bt::Color::namedColor(display, 0, back);
+
+  bt::Texture texture;
+  texture.setDescription(descr);
+  texture.setColor1(f);
+  texture.setColor2(b);
+
+  for (unsigned int screen = 0; screen < display.screenCount(); screen++) {
+    const bt::ScreenInfo &screen_info = display.screenInfo(screen);
+
+    bt::Image image(screen_info.width(), screen_info.height());
+    Pixmap pixmap = image.render(display, screen, texture);
+
+    XSetWindowBackgroundPixmap(display.XDisplay(),
+                               screen_info.rootWindow(),
+                               pixmap);
+    XClearWindow(display.XDisplay(), screen_info.rootWindow());
+
+    setPixmapProperty(screen, pixmap);
+  }
+}
+
+
+void bsetroot::usage(int exit_code) {
+  fprintf(stderr,
+          "bsetroot 3.1\n\n"
+          "Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry\n"
+          "Copyright (c) 1997 - 2000, 2002 - 2005 Bradley T Hughes\n");
+  fprintf(stderr,
+          "  -display <string>        use display connection\n"
+          "  -mod <x> <y>             modula pattern\n"
+          "  -foreground, -fg <color> modula foreground color\n"
+          "  -background, -bg <color> modula background color\n\n"
+          "  -gradient <texture>      gradient texture\n"
+          "  -from <color>            gradient start color\n"
+          "  -to <color>              gradient end color\n\n"
+          "  -solid <color>           solid color\n\n"
+          "  -help                    print this help text and exit\n");
+  exit(exit_code);
+}
+
+int main(int argc, char **argv) {
+  char *display_name = 0;
+  bool multi_head = False;
+
+  for (int i = 1; i < argc; i++) {
+    if (! strcmp(argv[i], "-display")) {
+      // check for -display option
+
+      if ((++i) >= argc) {
+        fprintf(stderr, "error: '-display' requires an argument\n");
+
+        ::exit(1);
+      }
+
+      display_name = argv[i];
+    } else if (! strcmp(argv[i], "-multi")) {
+      multi_head = True;
+    }
+  }
+
+  bsetroot app(argc, argv, display_name, multi_head);
+  return 0;
+}
diff --git a/.pc/030_bsetbg-bash-shebang.patch/util/bsetbg b/.pc/030_bsetbg-bash-shebang.patch/util/bsetbg
new file mode 100755
index 0000000..fa68e9d
--- /dev/null
+++ b/.pc/030_bsetbg-bash-shebang.patch/util/bsetbg
@@ -0,0 +1,499 @@
+#!/bin/sh
+
+# Copyright (c) 2000-2002 Timothy M. King (tmk@lordzork.com)
+#
+# 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
+# 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+PATH=$PATH:/usr/bin:/usr/local/bin:/usr/X11R6/bin
+
+img_apps="display xli xsetbg Esetroot qiv wmsetbg xv"
+
+display_full_cmd="display -geometry 800x600 -window root"
+display_tile_cmd="display -window root"
+display_center_cmd="display -backdrop -window root"
+display_default_cmd="$display_center_cmd"
+
+Esetroot_full_cmd="Esetroot -scale"
+Esetroot_tile_cmd="Esetroot"
+Esetroot_center_cmd="Esetroot -c"
+Esetroot_default_cmd="$Esetroot_center_cmd"
+
+wmsetbg_full_cmd="wmsetbg -s -S"
+wmsetbg_tile_cmd="wmsetbg -t"
+wmsetbg_center_cmd="wmsetbg -e"
+wmsetbg_default_cmd="$wmsetbg_center_cmd"
+
+qiv_full_cmd="qiv --root_s"
+qiv_tile_cmd="qiv --root_t"
+qiv_center_cmd="qiv --root"
+qiv_default_cmd="$qiv_center_cmd"
+
+xv_full_cmd="xv -max -smooth -root -quit"
+xv_tile_cmd="xv -root -quit"
+xv_center_cmd="xv -rmode 5 -root -quit"
+xv_default_cmd="$xv_center_cmd"
+
+xli_full_cmd="xli -fullscreen -onroot -quiet"
+xli_tile_cmd="xli -onroot -quiet"
+xli_center_cmd="xli -center -onroot quiet"
+xli_default_cmd="$xli_center_cmd"
+
+xsetbg_full_cmd="xsetbg -fullscreen"
+xsetbg_tile_cmd="xsetbg"
+xsetbg_center_cmd="xsetbg -center"
+xsetbg_default_cmd="$xsetbg_center_cmd"
+
+##################################
+
+me=`basename $0`
+version=2.1
+copyright="(c) 2000-$(date +%Y) by Timothy M. King (http://lordzork.com/)"
+config=$HOME/.bsetbgrc
+last_cmd_file=$HOME/.bsetbg_last_cmd
+refresh_cmd=xrefresh
+p=$me:
+
+quit() 
+{
+	[ "$1" ] && rc=$1 && shift 1
+	[ "$*" ] && echo -e $*
+	exit ${rc:-0}
+}
+
+bool() {
+	case $1 in
+		[yY][eE][sS]|1|[yY]|[tT][rR][uU][eE]|[oO][nN]) : ;;
+		*) return 1 ;;
+	esac
+}
+
+check_exe_in_path() 
+{
+	if [ -z "$1" ]; then
+    		return 1
+	elif [ -x "$(which $1 2>/dev/null)" ]; then
+    		return 0
+	elif [ -x "$(type $1 2>/dev/null)" ]; then
+    		return 0
+	else
+    		return 1
+	fi
+}
+
+help_msg() 
+{
+	cat <<EOF
+$me $version $copyright
+
+  -center <file>           center an image on the desktop
+  -tile <file>             tile an image on the desktop
+  -full <file>             stretch an image to fill the desktop
+  -exec <args> <file>      specify an external command to execute
+  
+  -app  <app>              specify the image application to use
+  -post <string>           arguments to be passed to the post-command
+  -debug                   prints commands without executing them
+EOF
+
+	# this is extremely lame, but probably more portable than grep -E   
+	bsetroot_help=$(bsetroot -help  2>&1| grep -v "^bsetroot" | grep -v "^  -help")
+	case $bsetroot_help in
+   		BaseDisplay*)	echo ;;
+		*-gradient*)	echo "$bsetroot_help"
+	esac
+
+	cat <<EOF
+  -generate <string>       generate a config file
+  -help                    this message
+  -version                 output version information
+EOF
+	quit ${1:-0}
+}
+
+get_apps() 
+{
+	for a in $img_apps; do
+    		if check_exe_in_path $a; then
+			eval center_cmd=\$$a\_center_cmd
+			eval full_cmd=\$$a\_full_cmd
+			eval tile_cmd=\$$a\_tile_cmd
+			eval default_cmd=\$$a\_default_cmd
+			return 0
+		else
+			if [ "$not_found" ]; then
+				not_found="$not_found $a"
+			else
+				not_found=$a
+			fi
+		fi
+	done
+	return 1
+}
+
+do_generate() 
+{
+	echo -e "# created by $me $version on $(date)\n#"
+	echo -e "# seting NO_EXEC to a boolean value (eg true/false) will cause $me"
+	echo -e "# to never modify the root window\n#"
+	echo -e "#NO_EXEC=\n#"
+	echo -e "# POST_COMMAND can be set to a command that will be run run every time"
+	echo -e "# $me sets the root image\n#"
+	echo -e "#POST_COMMAND=\n#"
+	echo -e "# if LOG_LAST_CMD is set (boolean), bsetbg will keep a log of the last"
+	echo -e "# two successful commands.\n#"
+	echo -e "#LOG_LAST_CMD=\n#"
+	echo -e "# the LOGFILE specifies the file that bsetbg uses when LOG_LAST_CMD"
+	echo -e "# is defined. this defaults to ~/.bsetbg_last_cmd .\n#"
+	echo -e "#LOGFILE=\n#"
+	echo -e "# the following are default configuration values for the most popular image"
+	echo -e "# programs. See the man page of the respective application for more info.\n" 
+
+	[ "$*" ] && img_apps="$*"
+
+	for a in $img_apps; do
+    		if check_exe_in_path $a; then
+			if ! bool $have_match; then
+				q='\"'
+				[ "$(eval echo \$$a\_center_cmd)" ] &&
+				eval echo CENTER=$q\$$a\_center_cmd$q &&
+			
+				[ "$(eval echo \$$a\_full_cmd)" ] &&
+				eval echo FULL=$q\$$a\_full_cmd$q &&
+			
+				[ "$(eval echo \$$a\_tile_cmd)" ] &&
+				eval echo TILE=$q\$$a\_tile_cmd$q &&
+			
+				[ "$(eval echo \$$a\_default_cmd)" ] &&
+				eval echo -e DEFAULT=$q\$$a\_default_cmd$q \\\\n &&
+			
+				have_match=1
+			else
+				[ "$(eval echo \$$a\_center_cmd)" ] && 
+				eval echo \\#CENTER=$q\$$a\_center_cmd$q
+			
+				[ "$(eval echo \$$a\_full_cmd)" ] &&
+				eval echo \\#FULL=$q\$$a\_full_cmd$q
+			
+				[ "$(eval echo \$$a\_tile_cmd)" ] &&
+				eval echo \\#TILE=$q\$$a\_tile_cmd$q
+			
+				[ "$(eval echo \$$a\_default_cmd)" ] &&
+				eval echo -e \\#DEFAULT=$q\$$a\_default_cmd$q \\\\n
+			fi
+		fi
+	done
+	quit 0
+}
+
+do_bsetroot() 
+{
+	if check_exe_in_path bsetroot; then
+		read_config
+
+		$debug bsetroot $* 2>/dev/null; rc=$?
+		
+		if [ "$rc" -gt 0 ]; then
+			help_msg $rc
+		else
+			log_cmd bsetroot $*; $refresh_cmd 2>/dev/null
+		fi
+	else
+    		quit 1 "couldn't find bsetroot in $PATH"
+	fi
+}
+
+do_standard() 
+{
+	[ -z "$1" ] && help_msg 1
+
+	bool $noconfig || read_config
+
+	get_img_command $1
+	check_img_command $do_this
+
+	case $# in
+    		1) file="$1" ;;
+		*) file="$2"
+	esac
+
+	if [ -f "$file" ]; then
+    		exec_img_command $do_this $file 
+	else
+    		quit 1 "$file does not exist"
+	fi
+} 
+
+do_exec() 
+{
+	[ "$#" -lt 3 ] && help_msg 3
+
+	bool $noconfig || read_config
+    
+	# check to see if -*c, -*f, or -*t were spcified, if so
+	# assume the last argument is a filename
+	b_arg=$(eval echo \$$(( $# - 1 )) )
+	app=$1
+
+	case $b_arg in
+    		-c|*-center|c|-t|*-tile*|t|-f|*-full|f)
+			eval file=\$$#
+			f_args="$b_arg $file"
+	esac
+
+	# put the rest of the arguments into the varialbe $e_args
+	while [ "$*" ]; do
+    		for a in "$*"; do
+    			case $1 in
+				$b_arg|$file) : ;;
+				*) e_args="$e_args "$1""
+			esac
+			shift 1
+    		done
+	done
+
+	# with $f_args defined, check for image and do things normally
+	if [ "$f_args" ]; then
+		[ ! -f "$file" ] && quit 1 "$file does not exist"
+	
+		if check_img_command $e_args; then
+    			do_this="$e_args"
+    		else
+			read_config
+    			get_img_command $f_args
+			check_img_command $do_this
+			echo "$p couldn't find '$app' in path, using $type command instead"
+    		fi
+	# without $f_args, run the command blindly if it's in $PATH
+	elif check_exe_in_path $e_args; then
+    		do_this="$e_args"
+	else
+    		quit 1 "$p unable to run the following command: $e_args"
+	fi
+
+	exec_img_command $do_this $file
+}    
+
+get_img_command() 
+{
+	case $1 in
+		*-full|-f|f)	type=full; do_this="$full_cmd" ;;
+		*-tile|-t|t)	type=tile; do_this="$tile_cmd" ;;
+		*-center|-c|c)	type=center; do_this="$center_cmd" ;;
+		*)		type=default; do_this="$default_cmd"
+	esac
+}
+
+check_img_command() 
+{
+	if check_exe_in_path $1; then
+    		do_this="$*"
+		rc=0
+	elif get_apps; then
+    		get_img_command $*
+		rc=1
+	else
+		quit 1 "$p couldn't find a suitable image program. tried the following:\\n
+		$(for a in $not_found; do echo "    $a\\n"; done)"
+	fi
+
+	if [ "$rc" -gt 0 -a -z "$e_args" ] && bool $read_config; then
+    		echo "$p couldn't find a suitable $type command in $config"
+	fi
+
+    return $rc
+}
+
+exec_img_command() 
+{
+	unset rc
+	command=$*
+
+	if [ "$debug" ]; then
+    		$debug $command >/dev/null 2>&1; rc=$?
+	else
+    		$command >/dev/null 2>&1; rc=$?
+	fi
+
+	if [ "$rc" -gt 0 ]; then
+    		echo "$p '$command' exited with status $rc"
+		get_apps
+		noconfig=1
+		parse_args ${f_args:-$my_args}
+		echo "$p using '$command' as $type"
+		$debug $command >/dev/null 2>&1; rc=$?
+		[ "$rc" =  0 ] && log_cmd $do_this $file && $refresh_cmd 2>/dev/null
+	else
+    		log_cmd $do_this $file; xrefresh 2>/dev/null
+	fi
+	return $rc
+}
+
+log_cmd() 
+{
+	bool $LOG_LAST_CMD || return 1
+	[ "$debug" ] && return 1
+	echo -e "$prev_cmd\n$*" >$last_cmd_file
+	return $?
+}
+
+read_config() 
+{
+	[ -f $config ] || return 1
+
+	if bool $read_config; then
+		unset read_config
+	else
+		read_config=1
+	fi
+
+	. $HOME/.bsetbgrc 2>/dev/null
+	check_no_exec
+
+	full_cmd=$FULL
+	center_cmd=$CENTER
+	tile_cmd=$TILE
+	default_cmd=$CENTER
+	last_cmd_file=${LOGFILE:-$last_cmd_file}
+    
+	bool $LOG_LAST_CMD && prev_cmd=$(tail -n 1 $last_cmd_file 2>/dev/null)    
+}
+
+check_no_exec() 
+{
+	bool $NO_EXEC && 
+	quit 0 "$p no_exec mode. the root window will not be modified."
+}
+
+post_command() 
+{
+	if [ -n "$POST_COMMAND" -a "$rc" -le 0 ]; then
+		if [ -n "$debug" ]; then
+			$debug "running post_command: $POST_COMMAND $post_args"
+		else
+			post_command_output=$($POST_COMMAND $post_args 2>&1); erc=$?
+			if [ "$erc" -gt 0 ]; then
+				echo "$p post-command '$POST_COMMAND $post_args' exited with status $erc"
+				[ -n "$post_command_output" ] &&
+					echo "$POST_COMMAND $post_args: $post_command_output"
+			fi
+		fi
+	fi
+}
+
+add_arg() 
+{
+	[ "$1" ] || return 1
+	if [ "$args" ]; then
+    		args="$args $1"
+	else
+    		args=$1
+	fi
+}  
+
+add_post_arg() 
+{
+	[ -z "$1" ] && return 1
+	if [ "$post_args" ]; then
+		post_args="$post_args $1"
+	else
+		post_args=$1
+	fi
+}
+
+check_cmd() 
+{
+	if [ -z "$command" ]; then
+		command=${2:-$1}; eval ${3:-${2:-$1}}=1
+	elif bool $do_post; then
+		add_post_arg ${1}
+	else
+		finished=1
+	fi 
+}
+
+parse_args() 
+{
+	case $1 in
+    		-d|*-debug|d)
+			unset refresh_cmd; debug=echo\ $me\_debug: ;;
+
+		-p|-*-post|p)			
+			unset finished do_standard do_exec; do_post=1 ;;
+				
+		-c|*-center|c|-t|*-tile*|t|-f|*-full|f)
+			case $command in
+				do_standard|do_generate|do_bsetroot)
+					finished=1 ;;
+				do_exec)
+					if ! bool $got_fcmd; then
+						add_arg $1 args; got_fcmd=1
+					else
+						finished=1
+					fi ;;
+				*)	
+					add_arg $1; do_standard=1; command=do_standard
+			esac ;;		
+		
+		-a|*-app|a|-e|*-exec|e)
+			check_cmd "do_exec" ;;
+		
+		-mod|-gradient|-solid|-display)
+			check_cmd "do_bsetroot" && add_arg $1 ;;
+
+		-g|*-generate*|g)
+			check_cmd $1 "do_generate" ;;
+		
+		-v|*-version|v)
+			[ -z "$command" ] && quit 0 $me $version $copyright ;;
+		
+		-h|*-help|h)
+			[ -z "$command" ] && help_msg ;;
+		
+		*)	
+			bool $finished && return 1		
+
+			case $1 in -*)
+				bool $do_exec || bool $do_bsetroot || bool $do_post || help_msg 1
+			esac
+		
+			if bool $do_standard || bool $do_exec || bool $do_bsetroot || bool $do_generate; then
+				add_arg $1
+			elif bool $do_post; then
+				add_post_arg $1
+			else
+				add_arg $1; command=do_standard; finished=1
+			fi
+	esac
+}
+
+[ -z "$*" ] && help_msg 1
+
+my_args=$*
+
+for arg in "$@"; do
+	parse_args "$arg"
+	shift 1
+done
+
+[ "$debug" ] && echo
+
+$command $args
+post_command $rc
+
+quit $rc
diff --git a/.pc/040_textpropertytostring-unconditional.patch/lib/Util.hh b/.pc/040_textpropertytostring-unconditional.patch/lib/Util.hh
new file mode 100644
index 0000000..fe8625b
--- /dev/null
+++ b/.pc/040_textpropertytostring-unconditional.patch/lib/Util.hh
@@ -0,0 +1,104 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Util.hh for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#ifndef __Util_hh
+#define __Util_hh
+
+#include <limits.h>
+#include <string>
+
+// forward declarations of X11 types
+typedef struct _XDisplay Display;
+typedef union _XEvent XEvent;
+typedef struct _XGC *GC;
+typedef struct _XOC *XFontSet;
+typedef struct _XftFont XftFont;
+typedef unsigned long Time;
+typedef unsigned long XID;
+typedef XID Atom;
+typedef XID Colormap;
+typedef XID Cursor;
+typedef XID Drawable;
+typedef XID Pixmap;
+typedef XID Window;
+
+namespace bt {
+
+  // XXX perhaps we could just call this SENTINEL?
+  const unsigned int BSENTINEL = UINT_MAX;
+
+  class NoCopy {
+  protected:
+    inline NoCopy(void)
+    { }
+  private:
+    NoCopy(const NoCopy&);
+    NoCopy& operator=(const NoCopy&);
+  };
+
+  class PointerAssassin {
+  public:
+    template<typename T>
+    inline void operator()(const T ptr) const
+    { delete ptr; }
+  };
+
+  inline bool within(int x, int y, int width, int height)
+  { return ((x >= 0 && x <= width) && (y >= 0 && y <= height)); }
+
+  void bexec(const std::string& command, const std::string& displaystring);
+
+  std::string basename(const std::string& path);
+  std::string dirname(const std::string& path);
+
+  // equivalent to the shell command 'mkdir -m mode -p path'
+  bool mkdirhier(const std::string &path, int mode = 0777);
+
+  std::string expandTilde(const std::string& s);
+
+  std::string itostring(unsigned long i);
+  std::string itostring(long i);
+
+  inline std::string itostring(unsigned int i)
+  { return itostring(static_cast<unsigned long>(i)); }
+
+  inline std::string itostring(int i)
+  { return itostring(static_cast<long>(i)); }
+
+  inline std::string itostring(unsigned short i)
+  { return itostring(static_cast<unsigned long>(i)); }
+
+  inline std::string itostring(short i)
+  { return itostring(static_cast<long>(i)); }
+
+  std::string tolower(const std::string &string);
+
+#ifdef _XUTIL_H_
+  std::string textPropertyToString(::Display *display,
+                                   ::XTextProperty& text_prop);
+#endif
+
+} // namespace bt
+
+#endif // __Util_hh
diff --git a/.pc/050_manpage-tidy-up.patch/doc/blackbox.1.in b/.pc/050_manpage-tidy-up.patch/doc/blackbox.1.in
new file mode 100644
index 0000000..870dce6
--- /dev/null
+++ b/.pc/050_manpage-tidy-up.patch/doc/blackbox.1.in
@@ -0,0 +1,1151 @@
+.\"
+.\" nroff source for blackbox.1 man page
+.\"
+.\" Copyright 2002 by R.B. "Brig" Young II <secretsaregood@yahoo.com>
+.\" Written using gvim, available at http://www.vim.org/
+.\"
+.\" See the file COPYING in the source directory 
+.\" root for License specifics and restrictions
+.\" 
+.\" Updated for Blackbox 0.65.0 release on 18 Sep 2002.
+.\"
+.\"
+.\" Indented preformat macro. 
+.de EX
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.\"
+.\" * * * * * Section * * * * *
+.\"
+.\" ***** SubSection *****
+.\"
+.TH blackbox 1 "September 18, 2002" "0.65.0"
+.\"
+.\" * * * * * NAME * * * * * 
+.\"
+.SH NAME
+blackbox \- a window manager for X11
+.\"
+.\" * * * * * SYNOPSIS * * * * * 
+.\"
+.SH SYNOPSIS
+.BR "blackbox" " \-help | \-version"
+.br
+.B blackbox 
+.RI "[ \-rc" " rcfile " "] [ \-display" " display " ]
+.\"
+.\" * * * * * DESCRIPTION * * * * * 
+.\"
+.SH DESCRIPTION
+
+.\" ----- overview -----
+Blackbox is a window manager for the Open Group's
+X Window System, Version 11 Release 6 and above.
+Its design is meant to be visually minimalist and fast.
+.PP
+.\" ----- usage overview -----
+Blackbox is similar to the NeXT interface and
+Windowmaker. Applications are launched using a
+menu which is accessed by right clicking on the
+root window. Workspaces, a system of virtual 
+desktops are controlled via a menu which is accessed 
+by middle clicking on the root window and by using 
+the toolbar. Individual windows can be controlled by 
+buttons on the title bar and more options are available 
+by right clicking on the title bar.
+.PP
+.\" ----- design overview -----
+Blackbox is able to generate beautiful window
+decorations on the fly at high speed. Themes,
+called styles in Blackbox terminology, are very
+flexible but the use of pixmaps has been 
+purposefully avoided to eliminate dependencies 
+and excess memory usage.
+.PP
+.\" ----- bbtools overview -----
+Blackbox itself does not directly handle key
+bindings like most other window managers. This
+task is handled by a separate utility called
+bbkeys. Although Blackbox has a built-in
+workspace (paging) system, bbpager, which provides
+a graphical pager, is popular with many users.
+bbkeys, bbpager and several other bbtools can be found
+by going to
+.EX 0
+.B http://bbtools.thelinuxcommunity.org/
+.EE
+.\" ----- slit overview -----
+The slit is an edge of the screen which can
+hold specially designed programs called dock
+apps (from Windowmaker). In addition, the 
+popular program gkrellm will also run in the slit. 
+There is a huge selection of dockapps available 
+and they run the gamut from must-have gadgets 
+to utterly useless (but cute and/or funny) eye candy. 
+.EX 0
+.B http://www.bensinclair.com/dockapp/
+.B http://dockapps.org/
+.EE
+.\"
+.\" * * * * * OPTIONS * * * * * 
+.\"
+.SH OPTIONS 
+Blackbox supports the following command line options: 
+.TP
+.\" ----- help -----
+.B \-help 
+Display command line options, compiled-in features, and exit.
+.TP
+.\" ----- version -----
+.B \-version
+Display version and exit.
+.TP
+.\" ----- rcfile -----
+.BI \-rc \ rcfile
+Use an alternate resource file.
+.TP 
+.\" ----- display -----
+.BI \-display \ display
+Start Blackbox on the specified display, and set the 
+.B DISPLAY 
+environment variable to this value for programs 
+started by Blackbox.
+.PP
+.\"
+.\" * * * * * STARTING AND EXITING BLACKBOX * * * * *
+.\"
+.SH STARTING AND EXITING BLACKBOX
+The most common method for starting Blackbox 
+is to place the the command "blackbox" (no quotes)
+at the end of your 
+.B ~/.xinitrc
+or
+.B ~/.xsession
+file. 
+The advantage of putting Blackbox at the end of the file
+is that the X Server will shutdown when you exit 
+Blackbox. Blackbox can also be started from 
+the command line of a terminal program like xterm in an
+X session that does not already have a window manager running.
+.PP
+On startup, Blackbox will look for 
+.B ~/.blackboxrc 
+and use the resource 
+.B session.menuFile 
+to determine where to get the menu for the session. 
+If this file is not found Blackbox will use
+.B @defaultmenu@
+as the menu file. If that fails as well Blackbox 
+will use a default menu that contains commands 
+to start an xterm as well as restart and exit the window manager.
+The other resources available in the 
+.B ~/.blackboxrc 
+file are discussed later in this manual under 
+the heading RESOURCE FILE.
+
+On exit, Blackbox writes its current configuration to 
+.B ~/.blackboxrc. 
+.EX 0
+.B NOTE: 
+If ~/.blackboxrc is modified during a Blackbox 
+session, Blackbox must be restarted with the 
+"restart" command on the main menu or the changes 
+will be lost on exit. Restart causes Blackbox to 
+re-read ~/.blackboxrc and apply the changes immediately.
+.EE
+Blackbox can be exited by selecting "exit" on
+the main menu (discussed shortly), killing it
+gently from a terminal or by the X Window System 
+shutdown hot key combo Ctrl+Alt+BackSpace. 
+.PP
+.\"
+.\" * * * * * USING BLACKBOX * * * * * 
+.\"
+.SH USING BLACKBOX
+.PP
+A three button mouse has the following functions 
+when clicking on the root window:
+.TP
+.BR "Button Two" "  (Middle Button)"
+Open workspace menu
+.TP
+.BR "Button Three" "  (Right Button)"
+Open main menu
+.TP
+Note that Button One (Left Button) is not used.
+.\"
+.\" ***** MAIN MENU ******
+.\"
+.TP
+.B Main Menu
+The default installation assumes you have a number 
+of common X Window System programs in their typical
+locations. The default menu is defined by a plain text
+file named 'menu'. It is heavily commented and covers a
+number of details of menu file syntax. This file can also
+be edited graphically by using the extension program bbconf
+which makes menu creation very easy. Menu file syntax is
+discussed later in this manual.
+.EX 0
+.\" ----- main menu caveat -----
+.B Caveat:
+Menus can run arbitrary command lines, but
+if you wish to use a complex command line 
+it is best to place it in a shell script. 
+Remember to put #!/bin/sh on the first 
+line and chmod 755 on the file to make it 
+executable.
+.EE
+.\"
+.\" ***** WORKSPACE MENU *****
+.\"
+.TP
+.B Workspace Menu
+This menu gives the user control of the workspace 
+system. The user can create a new workspace,
+remove the last workspace or go to an application
+via either the icon menu or a workspace entry.
+Workspaces are listed by name. Clicking on the 
+workspace name will take you to that workspace
+with focus on the program under the mouse. If 
+there are programs already running in the 
+workspace, they will appear in a pop-out menu. 
+Clicking on the application name will jump to 
+the workspace and focus that application. If a
+middle click is used the window will be brought to
+the current workspace.
+
+Blackbox uses an external program, bbpager,
+to provide a traditional, graphical paging
+interface to the workspace system. Many Blackbox
+users run another extension program - bbkeys -
+to provide keyboard shortcuts for workspace control.
+.EX 0
+.\" ----- workspace caveat -----
+.B Caveat: 
+To name a workspace the user must right
+click on the toolbar, select "Edit current 
+workspace name," type the workspace name, 
+And_Press_Enter to finish.
+.EE
+Workspaces can also be named in the .blackboxrc
+file as described in RESOURCES.
+.\"
+.\" ***** THE SLIT *****
+.\"
+.TP
+.B The Slit
+The Slit provides a user positionable window for 
+running utility programs called "dockapps". To 
+learn more about dockapps refer to the web sites
+mentioned in the Description. Dockapps 
+automatically run in the slit in most cases, but
+may require a special command switch.  
+Often, -w is used for "withdrawn" into the slit.
+
+gkrellm is a very useful and modern dockapp that
+gives the user near real time information on 
+machine performance. Other dockapps include clocks,
+notepads, pagers, key grabbers, fishbowls, fire
+places and many, many others. 
+
+Only mouse button three is captured by the 
+Blackbox slit. This menu allows the user to change 
+the position of the slit, and sets the state of 
+Always on top, and Auto hide. These all do what 
+the user expects.
+
+.EX 3
+.\" ----- slit caveat -----
+.B Caveat:
+When starting Dockapps from an external script
+a race condition can take place where the shell
+rapidly forks all of the dockapps, which then
+take varied and random times to draw themselves 
+for the first time. To get the dockapps to start 
+in a given order, follow each dockapp with 
+sleep 2; This ensures that each dockapp is placed 
+in the correct order by the slit.
+.EE
+.EX 8
+.B i.e.
+#!/bin/sh
+speyes -w & sleep 2
+gkrellm -w & sleep 2
+.EE 
+.\"
+.\" ***** THE TOOLBAR *****
+.\"
+.TP
+.B The Toolbar
+The toolbar provides an alternate method for 
+cycling through multiple workspaces and 
+applications. The left side of the toolbar is 
+the workspace control, the center is the 
+application control, and the right side is a
+clock. The format of the clock can be controlled
+as described under RESOURCES.
+
+Mouse button 3 raises a menu that allows 
+configuration of the toolbar. It can be 
+positioned either at the top or the bottom 
+of the screen and can be set to auto hide 
+and/or to always be on top.
+
+.EX 
+.\" ----- toolbar caveat -----
+.B Caveat: 
+The toolbar is a permanent fixture. It 
+can only be removed by modifying the source and 
+rebuilding, which is beyond the scope of this 
+document. Setting the toolbar to auto hide is
+the next best thing.
+.EE
+
+.\"
+.\" ***** WINDOW DECORATIONS *****
+.\"
+.\" ----- overview -----
+.TP 
+.B Window Decorations
+Window decorations include handles at the bottom of
+each window, a title bar, and three control buttons.
+The handles at the bottom of the window are divided 
+into three sections.  The two corner sections are 
+resizing handles The center section is a window 
+moving handle. The bottom center handle and the 
+title bar respond to a number of mouse clicks and 
+key + mouse click combinations. The three buttons
+in the title bar, left to right, are iconify, maximize,
+and close. The resize button has special behavior 
+detailed below.
+.\"
+.\" ----- mouse buttons -----
+.\"
+.TP
+.BR "Button One" "  (Left Button)" 
+Click and drag on titlebar to move or resize from bottom corners.
+Click the iconify button to move the window to the icon list.
+Click the maximize button to fully maximize the window.
+Click the close button to close the window and application.
+Double-Click the title bar to shade the window.
+.TP
+.BR "Alt + Button One" ""
+Click anywhere on client window and drag to move the window.
+.TP
+.BR "Button Two" "  (Middle Button)"
+Click the titlebar to lower the window.
+Click the maximize button to maximize the window vertically.
+.TP
+.BR "Button Three" "  (Right Button)"
+Click on title bar or bottom center handle pulls down a control menu.
+Click the maximize button to maximize the window horizontally.
+.TP
+.BR "Alt + Button Three" ""
+Click anywhere on client window and drag to resize the window.
+
+.TP
+.\"
+.\" ----- control menu -----
+.\"
+.B The control menu contains:
+.TP
+.B Send To ...
+.EX
+.BR "Button One" "  (Left Button)"
+Click to send this window to another workspace.  
+.EE
+.EX
+.BR "Button Two" "  (Middle Button)"
+Click to send this window to another workspace, change 
+to that workspace and keep the application focused.
+as well.
+.TP
+.B Shade
+This is the same action as Double-Click with Button One.
+.TP
+.B Iconify
+Hide the window.  It can be accessed with the icon menu.
+.TP
+.B Maximize
+Toggle window maximization.
+.TP
+.B Raise
+Bring window to the front above the other windows and
+focus it.
+.TP
+.B Lower
+Drop the window below the other ones.
+.TP
+.B Stick
+Stick this window to the glass on the inside of
+the monitor so it does not hide when you change 
+workspaces.
+.TP
+.B Kill Client
+This kills the client program with -SIGKILL (-9)
+Only use this as a last resort.
+.TP
+.B Close
+Send a close signal to the client application.
+.\"
+.\" * * * * * STYLES * * * * * 
+.\"
+.\" ----- overview -----
+.SH STYLES
+Styles are a collection of colors, fonts,
+and textures that control the appearance of
+Blackbox. These characteristics are recorded
+in style files. The default system style files
+are located in
+.I @pkgdatadir@/styles.
+The menu system will identify the style by 
+its filename, and styles can be sorted into
+different directories at the user's discretion.
+
+.\" ----- third party styles -----
+There are over 700 styles available for 
+Blackbox. The official distribution point for 
+Blackbox styles is
+
+.EX
+.B http://blackbox.themes.org/
+.EE
+
+.\"
+.\" ----- installing styles -----
+.\"
+All themes should install by simply downloading them 
+to 
+.B ~/.blackbox/
+then unzip it, and de-tar it.
+
+On open Unixes this will be:
+
+.B tar zxvf stylename.tar.gz
+
+On commercial Unixes this will be something like:
+
+.B gunzip stylename.tar.gz && tar xvf stylename.tar
+
+Check your system manuals for specifics or check with
+your network administrator.
+
+An entry should appear in the styles menu immediately.
+.EX
+.B Security Warning
+Style files can execute shell scripts and other
+executables. It would is wise to check the 
+rootCommand in the style file and make sure that 
+it is benign.
+.EE
+.TP
+.B Things that go wrong.
+.TP
+1. The theme is pre Blackbox 0.51.
+Style file syntax changed with version 0.51
+.TP
+2. The style tarball was formatted incorrectly.
+Some styles use the directories 
+.B ~/.blackbox/Backgrounds 
+and
+.B ~/.blackbox/Styles
+
+This can fixed by adding a 
+.B [stylemenu] (~/.blackbox/Styles)
+to your menu file. To be a complete purist, hack 
+the style file with the correct paths and move
+the files into the correct directories
+.TP
+3. The rootCommmand line is broken.
+The rootCommand line in the style file will run an 
+arbitrary executable. It is important that this
+executable be set to bsetbg to maintain portability 
+between systems with different graphics software. In
+addition bsetbg can execute a shell script and do it
+in a portable fashion as well. 
+.\"
+.\" ----- style format ------
+.\"
+.TP
+.B The documented method for creating styles is as follows:
+.\" ----- background image -----
+.TP
+1. Create or acquire the background for the style if
+it will not be using 
+.B bsetroot 
+to draw a patterned background for the root window. 
+
+.EX
+.B NOTE:
+Blackbox runs on a wide variety 
+of systems ranging from PCs with 640x480 256 color 
+displays to ultra high speed workstations with 25" 
+screens and extreme resolution. For best results a 
+style graphic should be at least 1024x768.
+.EE 
+.\" ----- style file ------
+.TP
+2. Create a style file. 
+The best way to do this is to make a copy of a 
+similar style and then edit it. 
+
+The style file is a list of X resources and other
+external variables. Manipulating these variables 
+allows users to completely change the appearance 
+of Blackbox. The user can also change the root 
+window image by using the wrapper program 
+.B bsetbg.
+
+bsetbg knows how to use a number of programs to
+set the root window image. This makes styles much
+more portable since various platforms have different 
+graphics software. For more info see 
+.B bsetbg (1).
+.\" ----- directory layout
+.TP
+3. Background images should be placed in
+.B ~/.blackbox/backgrounds
+The style file should be placed in
+.B ~/.blackbox/styles
+any other information about the style should 
+be placed in 
+.B ~/.blackbox/about/STYLE_NAME/.
+This would include README files, licenses, etc.
+
+Previous versions of Blackbox put backgrounds 
+and styles in different directories. The 
+directories listed above are the only officially 
+supported directories.  However you may put them
+whereever you like as long as you update your menu
+file so it knows where to find your styles.
+.\" ----- tarball -----
+.TP
+4. To create a consistent experience and to ensure
+portability between all systems it is important 
+to use the following format to create your style
+archive.
+
+first create a new directory named 
+after your style
+.B NEW_STYLE
+
+In this directory create the 
+directories
+.EX
+.B backgrounds
+.B styles
+.B about/NEW_STYLE
+.EE
+Next put everything for the theme 
+in these locations. Finally type
+
+tar cvzf NEW_STYLE.tar.gz *
+
+If you are using commercial Unix you may 
+need to use gzip and tar separately.
+
+Now when a user downloads a new style file
+she knows that all she has to do is put
+the tarball in her Blackbox directory,
+unzip->un-tar it and then click on it in her
+style menu.
+.TP
+.B
+.\" ----- X resources -----
+.SH Style File Syntax and Details
+
+By far the easiest way to create a new style is to 
+use bbconf. bbconf allows complete control of every 
+facet of style files and gives immediate updates of
+the current style as changes are made.
+
+The style file format is not currently documented in
+a man page.  There is a readme document included with
+the Blackbox source containing this information.
+.\"
+.\" * * * * * MENU FILE * * * * *
+.\"
+.\" ----- overview -----
+.SH MENU FILE
+The default menu file is installed in 
+.B @defaultmenu@.
+This menu can be customized as a system 
+default menu or the user can create a 
+personal menu.
+
+To create a personal menu copy the 
+default menu to a file in your home directory.
+Then, open 
+.B ~/.blackboxrc 
+and add or modify the resource
+.BI "session.menuFile:" "  ~/.blackbox/menu"
+
+Next, edit the new menu file. This can be done 
+during a Blackbox session and the menu will 
+automatically be updated when the code checks
+for file changes.
+
+The default menu included with Blackbox has 
+numerous comments describing the use of all 
+menu commands. Menu commands follow this general 
+form:
+
+.BI "[command]" "  (label|filename) {shell command|filename}" 
+.\"
+.\" ----- menu commands -----
+.\"
+.TP
+.B Blackbox menu commands:
+.TP
+.BI "   #    " "string..."
+Hash (or pound or number sign) is used as the comment delimiter. It can
+be used as a full line comment or as an end of 
+line comment after a valid command statement.
+.TP
+.BI "[begin]" "  (string)"
+This tag is used only once at the beginning of the 
+menu file. "string" is the name or description used
+at the top of the menu.
+.TP
+.BI "[end]       " "" 
+This tag is used at the end of the menu file
+and at the end of a submenu block.
+.TP
+.BI "[exec]" "  (label string) {command string}"
+This is a very flexible tag that allows the user
+to run an arbitrary shell command including shell
+scripts. If a command is too large to type on the 
+command line by hand it is best to put it in a 
+shell script. 
+.TP
+.BI "[nop]" "  (label string)"
+This tag is used to put a divider in the menu.
+.I label string
+is an optional description.
+.TP
+.BI "[submenu]" "  (submenu name) {title string}"
+This creates a sub-menu with the name
+.I submenu name
+and if given, the string
+.I title string
+will be the title of the pop up menu itself.
+.TP
+.BI "[include]" "  (filename)"
+This command inserts
+.I filename
+into the menu file at the point at which it is
+called. 
+.I filename
+should not contain a begin end pair. This feature
+can be used to include the system menu or include a
+piece of menu that is updated by a separate program.
+.TP
+.BI "[stylesdir]" "  (description) (path)"
+Causes Blackbox to search 
+.I path
+for style files. Blackbox lists styles in the menu
+by their file name as returned by the OS.
+.TP
+.BI "[stylesmenu]" "  (description) {path}"
+This command creates a submenu with the name
+.B description
+with the contents of 
+.B path.
+By creating a submenu and then populating it 
+with stylesmenu entries the user can create an
+organized library of styles.
+.TP
+.BI "[workspaces]" "  (description)"
+Inserts a link into the main menu to the workspace
+menu. If used,
+.I description
+is an optional description.
+.TP
+.BI "[config]" "  (label)"
+This command causes Blackbox to insert a menu that
+gives the user control over focus models, dithering
+and other system preferences.
+.TP
+.BI "[reconfig]" "  (label) {shell command}"
+The reconfig command causes Blackbox to reread its 
+configuration files. This does not include 
+.B ~/.blackboxrc
+which is only reread when Blackbox is restarted. If
+.I shell command
+is included Blackbox will run this command or 
+shell script before rereading the files. This can 
+be used to switch between multiple configurations
+.TP
+.BI "[restart]" "  (label) {shell command}"
+This command is actually an exit command that
+defaults to restarting Blackbox. If provided
+.B shell command
+is run instead of Blackbox. This can be used
+to change versions of Blackbox. Not that you would
+ever want to do this but, it could also be used
+to start a different window manager. 
+.TP
+.BI "[exit]" "  (label)"
+Shuts down Blackbox. If Blackbox is the last command in your
+.B ~/.xinitrc
+file, this action will also shutdown X. 
+.EX 
+.B Here is a working example of a menu file:
+.\" ----- menu example -----
+[begin] (MenuName)
+   [exec] (xterm) {xterm -ls -bg black -fg green}
+   [submenu] (X utilities)
+      [exec] (xcalc) {xcalc}
+   [end]
+   [submenu] (styles)
+      [stylesmenu] (built-in styles) {@pkgdatadir@/styles}
+      [stylesmenu] (custom styles) {~/.blackbox/styles}
+   [end]
+   [workspaces] (workspace list)
+   [config] (configure)
+   [reconfig] (config play desktop) {play-config-blackbox}
+   [reconfig] (config work desktop) {work-config-blackbox}
+   [restart] (start Blackbox beta 7) {blackbox-beta7}
+   [restart] (start Blackbox cvs) {blackbox-cvs}
+   [restart] (restart)
+   [exit] (exit)
+[end]
+.EE
+.\"
+.\" * * * * * RESOURCE FILE * * * * * 
+.\"
+.SH RESOURCE FILE     
+.BI "$HOME" "/.blackboxrc"
+.\" ----- overview -----
+These options are stored in the ~/.blackboxrc file.
+They control various features of Blackbox and most
+can be set from menus. Some of these can
+only be set by editing .blackboxrc directly.
+
+NOTE: Blackbox only reads this file during start
+up. To make changes take effect during a Blackbox
+session the user must choose "restart" on the main menu.
+If you do not do so, your changes will be lost when
+Blackbox exits.
+
+Some resources are named with a <num> after screen. This
+should be replaced with the number of the screen
+that is being configured. The default is 0 (zero).
+.\" ----- resource keys -----
+.\"
+.\" ***** MENU CONFIGURABLE FROM SLIT MENU *****
+.\"
+.TP 3
+.IB "Menu Configurable" "  (Slit Menu):"
+Right click (button 3) on the slit border.
+.TP 3
+.BI "session.screen<num>.slit.placement" "  SEE BELOW"
+Determines the position of the slit.
+Certain combinations of slit.placement with
+slit.direction are not terribly useful, i.e. TopCenter
+with Vertical direction puts the slit through the
+middle of your screen. Certainly some will think that
+is cool if only to be different...
+.EX
+.B Default is CenterLeft.
+[  TopLeft  |   TopCenter  |   TopRight  | 
+ CenterLeft |              | CenterRight |
+ BottomLeft | BottomCenter | BottomRight ]
+.EE
+.TP 3
+.BI "session.screen<num>.slit.direction" "  [Horizontal|Vertical]"
+Determines the direction of the slit.
+.EX
+.B Default is Vertical.
+.EE
+.TP 3
+.BI "session.screen<num>.slit.onTop" "  [True|False]"
+Determines whether the slit is always visible
+over windows or if the focused window can hide
+the slit.
+.EX 
+.B Default is True.
+.EE
+.TP 3
+.BI "session.screen<num>.slit.autoHide" "  [True|False]"
+Determines whether the slit hides when not in use.
+The session.autoRaiseDelay time determines how long you
+must hover to get the slit to raise and how long it
+stays visible after mouse out.
+.EX
+.B Default is False.
+.EE
+.\"
+.\" ***** MENU CONFIGURABLE FROM MAIN MENU *****
+.\"
+.TP 3
+.IB "Menu Configurable" "  (Main Menu):"
+.TP 3
+.BI "session.screen<num>.focusModel" "  SEE BELOW"
+Sloppy focus (mouse focus) is the conventional X Window
+behavior and can be modified with AutoRaise or
+Click-Raise.
+
+AutoRaise causes the window to automatically raise after
+session.autoRaiseDelay milliseconds.
+
+ClickRaise causes the window to raise if you click
+anywhere inside the client area of the window.
+
+Sloppy focus alone requires a click on the titlebar,
+border or lower grip to raise the window.
+
+ClickToFocus requires a click on a Blackbox decoration
+or in the client area to focus and raise the window.
+ClickToFocus cannot be modified by AutoRaise or 
+ClickRaise.
+.EX
+.B Default is SloppyFocus
+[SloppyFocus [[AutoRaise & ClickRaise]  |
+              [AutoRaise | ClickRaise]] | 
+ClickToFocus]
+.EE
+.TP 3
+.BI "session.screen<num>.windowPlacement" "  SEE BELOW"
+RowSmartPlacement tries to fit new windows in empty space
+by making rows.
+Direction depends on session.screen<num>.rowPlacementDirection
+
+ColSmartPlacement tries to fit new windows in empty space
+by making columns
+Direction depends on session.screen<num>.colPlacementDirection
+
+CascadePlacement places the new window down and to
+the right of the most recently created window.
+.EX
+.B Default is RowSmartPlacement.
+[RowSmartPlacement | ColSmartPlacement | CascadePlacement]
+.EE
+.TP 3
+.BI "session.screen<num>.rowPlacementDirection" "  [LeftToRight|RightToLeft]"
+Determines placement direction for new windows.
+.EX
+.B Default is LeftToRight.
+.EE
+.TP 3
+.BI "session.screen<num>.colPlacementDirection" "  [TopToBottom|BottomToTop]"
+Determines placement direction for new windows.
+.EX
+.B Default is TopToBottom.
+.EE
+.TP 3
+.BI "session.imageDither" "  [True|False]" 
+This setting is only used when running in low 
+color modes. Image Dithering helps to show an
+image properly even if there are not enough
+colors available in the system.
+.EX 
+.B Default is False.
+.EE
+.TP 3
+.BI "session.opaqueMove" "  [True|False]"
+Determines whether the window's contents are drawn as it is moved.  When
+False the behavior is to draw a box representing the window.
+.EX
+.B Default is False.
+.EE
+.TP 3
+.BI "session.screen<num>.fullMaximization" "  [True|False]"
+Determines if the maximize button will cause an application
+to maximize over the slit and toolbar.
+.EX 
+.B Default is False.
+.EE
+.TP 3
+.BI "session.screen<num>.focusNewWindows" "  [True|False]"
+Determines if newly created windows are given focus after
+they initially draw themselves.
+.EX
+.B Default is False. 
+.EE 
+.TP
+.BI "session.screen<num>.focusLastWindow" "  [True|False]"
+This is actually "when moving between workspaces, remember
+which window has focus when leaving a workspace and return
+the focus to that window when I return to that workspace."
+.EX
+.B Default is False.
+.EE
+.TP
+.BI "session.screen<num>.disableBindingsWithScrollLock" "  [True|False]"
+When this resource is enabled, turning on scroll lock
+keeps Blackbox from grabbing the Alt and Ctrl keys
+that it normally uses for mouse controls. This feature
+allows users of drawing and modeling programs which use
+keystrokes to modify mouse actions to maintain their sanity.
+*NOTE* this has _no_ affect on bbkeys.  If you need bbkeys to also
+behave this way it has a similar option in its config file.  Refer
+to the bbkeys manpage for details.
+.EX
+.B Default is False.
+.EE
+.\"
+.\" ***** MENU CONFIGURABLE FROM WORKSPACE MENU *****
+.\"
+.TP
+.IB "Menu Configurable" "  (Workspace Menu):"
+Middle click (button 2) on the root window (AKA Desktop) 
+to reach this menu
+.TP 3
+.BI "session.screen<num>.workspaces" "  [integer]"
+Workspaces may be created or deleted by middle clicking
+on the desktop and choosing "New Workspace" or "Remove
+Last". After creating a workspace, right click on the
+toolbar to name it.
+.EX
+.B Default is 1
+.EE
+.\"
+.\" ***** MENU CONFIGURABLE FROM TOOLBAR MENU *****
+.\"
+.TP
+.IB "Menu Configurable" "  (Toolbar Menu):"
+.TP 3
+.BI "session.screen<num>.workspaceNames" "  [string[, string...]]"
+Workspaces are named in the order specified in this
+resource. Names should be delimited by commas. If there
+are more workspaces than explicit names, un-named 
+workspaces will be named as "Workspace [number]".
+.EX
+.B Default is 
+Workspace 1.
+.EE
+.TP 3
+.BI "session.screen<num>.toolbar.placement" "  SEE BELOW"
+Set toolbar screen position.
+.EX 
+.B Default is BottomCenter
+[  TopLeft  |   TopCenter  |   TopRight  | 
+ BottomLeft | BottomCenter | BottomRight ]
+.EE
+.TP 3
+.BI "session.screen<num>.toolbar.onTop" "  [True|False]"
+Determines whether the toolbar is always visible
+over windows or if the focused window can hide
+the toolbar.
+.EX
+.B Default is True.
+.EE
+.TP 3
+.BI "session.screen<num>.toolbar.autoHide" "  [True|False]"
+Determines whether the toolbar hides when not in use.
+The session.autoRaiseDelay time determines how long you
+must hover to get the toolbar to raise, and how long it
+stays visible after mouse out.
+.EX
+.B Default is False.
+.EE
+.\"
+.\" ***** CONFIGURABLE IN BLACKBOXRC ONLY *****
+.\"
+.TP 3
+.IB "Configurable in" "  ~/.Blackboxrc only:"
+.TP 3
+.BI "session.screen<num>.toolbar.widthPercent" "  [1-100]"
+Percentage of screen used by the toolbar.
+A number from 1-100 that sets the width of the toolbar.
+0 (zero) does not cause the toolbar to disappear, instead
+the toolbar is set to the default. If you want to lose the
+toolbar there are patches that can remove it.
+.EX
+.B Default is 66.
+.EE
+.TP 3
+.BI "session.screen<num>.strftimeFormat" "  [string]"
+A C language date format string, any combination of
+specifiers can be used. The default is %I:%M %p which
+generates a 12 hour clock with minutes and an am/pm
+indicator appropriate to the locale.
+.EX
+.IB "24 hours and minutes" "    %H:%M"
+.IB "12 hours and minute" "     %I:%M %p"
+.IB "month/day/year" "          %m/%d/%y"
+.IB "day/month/year" "          %d/%m/%y"
+.EE
+.EX
+.B Default is hours:minutes am/pm
+See
+.B strftime 3
+for more details.
+.EE
+.TP 3
+.BI "session.screen<num>.dateFormat" "  [American|European]"
+NOTE: Only used if the strftime() function is not
+available on  your system.
+.EX
+.B Default is American, (mon/day/year).
+.EE
+.TP 3
+.BI "session.screen<num>.clockFormat" "  [12/24]"
+.B NOTE: 
+Only used if the strftime() function is not
+available on your system.
+.EX 0
+.B Default is 12-hour format.
+.EE
+.TP 3
+.BI "session.screen<num>.edgeSnapThreshold" "  [integer]"
+When set to 0 this turns off edge snap. When set to one
+or greater edge snap will cause a window that is being
+moved to snap to the nearest screen edge, the slit, or
+or the toolbar. Windows will not snap to each other.
+The value represents a number in pixels which is the distance
+between the window and a screen edge which is required before
+the window is snapped to the screen edge.  If you prefer this
+functionality values between 6 - 10 work nicely.
+.EX
+.B Default value is 0
+.EE
+.TP 3
+.BI "session.menuFile" "  [filepath]"
+Full path to the current menu file.
+.EX
+.B Default is @defaultmenu@
+.EE
+.TP 3
+.BI "session.colorsPerChannel" "  [2-6]"
+The number of colors taken from the X server for use on
+pseudo color displays. This value must be set to 4 for
+8 bit displays.
+.EX
+.B Default is 4.
+.EE
+.TP 3
+.BI "session.doubleClickInterval" "  [integer]"
+This is the maximum time that Blackbox will wait after
+one click to catch a double click. This only applies to
+Blackbox actions, such as double click shading, not to the X
+server in general.
+.EX
+.B Default is 250 milliseconds.
+.EE
+.TP 3
+.BI "session.autoRaiseDelay" "  [integer]"
+This is the time in milliseconds used for auto raise
+and auto hide behaviors. More than about 1000 ms is
+likely useless.
+.EX
+.B Default is 250 millisecond.
+.EE
+.TP 3
+.BI "session.cacheLife" "  [integer]"
+Determines the maximum number of minutes that the X server
+will cache unused decorations.
+.EX 
+.B Default is 5 minutes
+.EE
+.TP 3
+.BI "session.cacheMax" "  [integer]"
+Determines how many kilobytes that Blackbox may take 
+from the X server for storing decorations. Increasing 
+this number may enhance your performance if you have 
+plenty of memory and use lots of different windows.
+.EX 
+.B Default is 200 Kilobytes
+.EE
+
+.\"
+.\" * * * * * ENVIRONMENT * * * * * 
+.\"
+.SH ENVIRONMENT
+.TP
+.B HOME
+Blackbox uses $HOME to find its .blackboxrc  
+rc file and its .blackbox directory for menus
+and style directories.
+.TP
+.B DISPLAY
+If a display is not specified on the command line, 
+Blackbox will use the value of $DISPLAY.
+
+.\"
+.\" * * * * * FILES * * * * * 
+.\"
+.SH FILES
+.TP
+.B blackbox
+Application binary
+.TP
+.B ~/.blackboxrc
+User's startup and resource file.
+.TP
+.B @defaultmenu@
+Default system wide menu
+	
+.\"
+.\" * * * * * WEB SITES * * * * * 
+.\"
+.SH WEB SITES
+.TP 5
+.\" ----- general info -----
+.B General info website:
+http://blackboxwm.sourceforge.net/
+.TP 5
+.\" ----- development info -----
+.B Development website:
+http://sourceforge.net/projects/blackboxwm/
+
+.\"
+.\" * * * * * BUGS * * * * * 
+.\"
+.SH BUGS
+If you think you have found a bug, please help by going
+to the development website and select "Bugs" in the upper
+menu. Check the bug list to see if your problem has already
+been reported. If it has please read the summary and add any
+information that you believe would help. If your bug has not been 
+submitted select "Submit New" and fill out the form.
+
+.\"
+.\" * * * * * AUTHORS AND HISTORY * * * * * 
+.\"
+.SH AUTHORS AND HISTORY 
+.\" ----- software authors -----
+.B Sean "Shaleh" Perry 
+.I " <shaleh@debian.org>" 
+is the current maintainer and is actively working 
+together with Brad to keep Blackbox up-to-date and 
+stable as a rock.
+
+.BI "Brad Hughes" "  <bhughes@trolltech.com>"
+originally designed and coded Blackbox in 1997 with 
+the intent of creating a memory efficient window 
+manager with no dependencies on external libraries. 
+Brad's original idea has become a popular alternative 
+to other window managers.
+
+.BI "Jeff Raven" "  <jraven@psu.edu>"
+then picked up the torch for the 0.61.x series after 
+Brad took a full time job at TrollTech.
+
+.\" ----- man page author -----
+This manual page was written by:
+.B R.B. "Brig" Young 
+.I " <secretsaregood@yahoo.com>" 
+he is solely responsible for errors or omissions. 
+Comments, corrections, and suggestions are welcomed.
+
+.\"
+.\" * * * * * SEE ALSO * * * * * 
+.\"
+.SH SEE ALSO
+.EX 
+bsetbg(1), bsetroot(1), 
+bbkeys(1), bbconf(1)
+.EE 
diff --git a/.pc/050_manpage-tidy-up.patch/doc/bsetbg.1 b/.pc/050_manpage-tidy-up.patch/doc/bsetbg.1
new file mode 100644
index 0000000..0b2115a
--- /dev/null
+++ b/.pc/050_manpage-tidy-up.patch/doc/bsetbg.1
@@ -0,0 +1,142 @@
+.TH bsetbg 1 "February 2002" "bsetbg" "v2.0"
+.SH NAME
+bsetbg \- utility to manipulate the appearance of the X11 desktop's root window.
+.SH SYNOPSIS
+\fBbsetbg\fR [options] \fIimage\fR
+.SH DESCRIPTION
+\fBbsetbg\fR is intended to provide a standard method for the \fIBlackbox\fR
+window manager to alter the background of the root window
+(although it will work with any other window manager as well). \fBbsetbg\fR
+acts as a wrapper both to \fIbsetroot\fR
+and to a flexible variety of third-party applications that it uses when handling images files.
+
+.SH OPTIONS
+.TP
+\fB\-f\fR, \fB\-full\fR \fIimage\fR
+\fIimage\fR is stretched to fill the entire desktop.
+.TP
+\fB\-t\fR, \fB\-tile\fR \fIimage\fR
+\fIimage\fR is tiled on the desktop.
+.TP
+\fB\-c\fR, \fB\-center\fR \fIimage\fR
+\fIimage\fR is centered on the desktop.
+.TP
+\fB\-e\fR, \fB\-exec\fR \fIprogram\fR \fIoptions\fR \fIfallback\-\fIarg\fR \fIimage\fR
+This option tells \fBbsetbg\fR to run a separate command by executing \fIprogram\fR with 
+\fIoptions\fR, where \fIoptions\fR are arguments to \fIprogram\fR.
+
+If a \fIfallback\-arg\fR is supplied (\fB\-full\fR, \fB\-tile\fR, or \fB\-center\fR
+as described above),
+\fBbsetbg\fR will assume that the last argument is a filename. In the case that
+\fIprogram\fR exits non-zero or isn't available on the target system, \fBbsetbg\fR 
+will try to handle the file with the fallback argument.
+
+See the \fBEXAMPLES\fR section for more information on \fB\-exec\fR.
+.TP
+\fB\-p\fR, \fB\-post\fR \fIlist\fR
+Specifies a list of arguments to pass to the $POST_COMMAND.
+.TP
+\fB\-d\fR, \fB\-debug\fR
+Debugging mode. \fBbsetbg\fR will print commands without executing them.
+.TP
+\fB\-g\fR, \fB\-generate\fR \fIlist\fR
+Output a list of default configuration values, suitable for redirecting into 
+\fI~/.bsetbgrc\fR. Any arguments that are supplied will be considered applications 
+to search for in the system path, overriding \fBbsetbg\fR's internal defaults.
+.TP
+\fB\-app\fR \fIimageApp\fR
+Use this flag to specify which image application to use. This
+application may be one of the pre-defined list or any application
+capable of displaying an image on the root window. This flag may be
+used in conjunction with passing application specific  parameters to
+the application, in which
+case they should be enclosed in double quotes.
+.TP
+\fB\-v\fR, \fB\-version\fR
+Output version number.
+.TP
+\fB\-h\fR, \fB\-help\fR
+Output a brief usage message.
+
+.SH OTHER OPTIONS
+\fBbsetbg\fR will also accept all of the arguments for \fIbsetroot\fR.
+Consult the \fIbsetroot\fR(1) man page for further information.
+
+.SH CONFIGURATION
+\fBbsetbg\fR will read its configuration values from the file \fI~/.bsetbgrc\fR
+if it exists. Otherwise, it will scan the 
+system path for a pre-defined list of image applications to use 
+(currently this list consists of qiv, xli, xv, wmsetbg, Esetroot, 
+display, and xsetbg).
+\fP
+\fI~/.bsetbgrc\fR should contain the following variables:
+.TP
+\fB    CENTER=\fR\fI"string"\fR
+Application and arguments to be used to center an image on the root window
+when the \fB-center\fR argument is specified.
+
+.TP
+\fB    FULL=\fR\fI"string"\fR
+Application and arguments to be used to stretch an image to fill the root window
+when the \fB-full\fR argument is specified.
+
+.TP
+\fB    TILE=\fR\fI"string"\fR
+Application and arguments to be used to tile an image on the root window
+when the \fB-tile\fR argument is specified.
+
+.TP
+\fB    DEFAULT=\fR\fI"string"\fR
+Action to take place by default if none of the above have been specified.
+
+.TP
+The following variables are optional:
+
+.TP
+\fB    NO_EXEC=\fR\fI"boolean"\fR
+If this variable is set, bsetbg will never modify the root window.
+
+.TP
+\fB    POST_COMMAND=\fR\fI"string"\fR
+This variable specifies a command that \fBbsetbg\fR will run after every
+successful modification of the root window.
+
+.TP
+\fB    LOG_LAST_CMD=\fR\fI"boolean"\fR
+If this variable is set, \fBbsetbg\fR will keep a logfile of the last two
+successful commands.
+
+.TP
+\fB    LOGFILE=\fR\fI"string"\fR
+This variable can specify the logfile to be used when $LOG_LAST_CMD is defined.
+The default is ~/.bsetbg_last_cmd .
+
+.TP
+As mentioned above, \fBbsetbg\fR will function perfectly for the majority of users without having a configuration file. Power users who want more control over \fBbsetbg\fR's behavior should run \fBbsetbg -g\fR and use the output to create a \fI~/.bsetbgrc\fR which may then be tweaked by hand.
+
+.SH EXAMPLES
+In this example, bsetbg will set the image in centered mode:
+
+    bsetbg -center foo.png
+
+An example of the \fB-exec\fR argument:
+
+    bsetbg -exec xv -root -quit -rmode 5 -rbg rgb:2/2/2 \\
+	-center foo.png
+
+An example in which bsetbg creates a configuration file using xv and qiv:
+
+	bsetbg -g xv qiv > ~/.bsetbgrc
+
+An example of the use of the \fB-app\fR argument:
+
+    bsetbg  -app qiv "-o rgb:d6/c5/a2 -x" -c foo.png
+
+.SH AUTHOR
+The author of
+.B bsetbg
+may be reached at \fItmk@lordzork.com\fR.
+
+.SH SEE ALSO
+\fIblackbox\fR(1), \fIbsetroot\fR(1), \fIqiv\fR(1), \fIxli\fR(1), \fIxv\fR(1), \fIdisplay\fR(1), 
+\fIwmsetbg\fR(1)
diff --git a/.pc/060_fix-spelling-errors.patch/nls/C/blackbox.m b/.pc/060_fix-spelling-errors.patch/nls/C/blackbox.m
new file mode 100644
index 0000000..74d09f6
--- /dev/null
+++ b/.pc/060_fix-spelling-errors.patch/nls/C/blackbox.m
@@ -0,0 +1,6 @@
+$set 13 #blackbox
+
+$ #NoManagableScreens
+# Blackbox::Blackbox: no managable screens found, aborting\n
+$ #MapRequest
+# Blackbox::process_event: MapRequest for 0x%lx\n
diff --git a/.pc/060_fix-spelling-errors.patch/nls/ko_KR/blackbox.m b/.pc/060_fix-spelling-errors.patch/nls/ko_KR/blackbox.m
new file mode 100644
index 0000000..74d09f6
--- /dev/null
+++ b/.pc/060_fix-spelling-errors.patch/nls/ko_KR/blackbox.m
@@ -0,0 +1,6 @@
+$set 13 #blackbox
+
+$ #NoManagableScreens
+# Blackbox::Blackbox: no managable screens found, aborting\n
+$ #MapRequest
+# Blackbox::process_event: MapRequest for 0x%lx\n
diff --git a/.pc/060_fix-spelling-errors.patch/nls/zh_TW/blackbox.m b/.pc/060_fix-spelling-errors.patch/nls/zh_TW/blackbox.m
new file mode 100644
index 0000000..74d09f6
--- /dev/null
+++ b/.pc/060_fix-spelling-errors.patch/nls/zh_TW/blackbox.m
@@ -0,0 +1,6 @@
+$set 13 #blackbox
+
+$ #NoManagableScreens
+# Blackbox::Blackbox: no managable screens found, aborting\n
+$ #MapRequest
+# Blackbox::process_event: MapRequest for 0x%lx\n
diff --git a/.pc/060_fix-spelling-errors.patch/src/blackbox.cc b/.pc/060_fix-spelling-errors.patch/src/blackbox.cc
new file mode 100644
index 0000000..d750fe6
--- /dev/null
+++ b/.pc/060_fix-spelling-errors.patch/src/blackbox.cc
@@ -0,0 +1,624 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// blackbox.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh at debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "blackbox.hh"
+#include "Screen.hh"
+#include "Slit.hh"
+#include "Window.hh"
+
+#include <Pen.hh>
+#include <PixmapCache.hh>
+#include <Util.hh>
+
+#include <X11/Xresource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+
+// #define FOCUS_DEBUG
+#ifdef FOCUS_DEBUG
+static const char *Mode[] = {
+  "Normal",
+  "Grab",
+  "Ungrab",
+  "WhileGrabbed"
+};
+
+static const char *Detail[] = {
+  "Ancestor",
+  "Virtual",
+  "Inferior",
+  "Nonlinear",
+  "NonlinearVirtual",
+  "Pointer",
+  "PointerRoot",
+  "DetailNone"
+};
+#endif // FOCUS_DEBUG
+
+
+void Blackbox::save_rc(void)
+{ _resource.save(*this); }
+
+
+void Blackbox::load_rc(void)
+{ _resource.load(*this); }
+
+
+void Blackbox::reload_rc(void) {
+  load_rc();
+  reconfigure();
+}
+
+
+void Blackbox::init_icccm(void) {
+  char* atoms[7] = {
+    "WM_COLORMAP_WINDOWS",
+    "WM_PROTOCOLS",
+    "WM_STATE",
+    "WM_CHANGE_STATE",
+    "WM_DELETE_WINDOW",
+    "WM_TAKE_FOCUS",
+    "_MOTIF_WM_HINTS"
+  };
+  Atom atoms_return[7];
+  XInternAtoms(XDisplay(), atoms, 7, false, atoms_return);
+  xa_wm_colormap_windows = atoms_return[0];
+  xa_wm_protocols = atoms_return[1];
+  xa_wm_state = atoms_return[2];
+  xa_wm_change_state = atoms_return[3];
+  xa_wm_delete_window = atoms_return[4];
+  xa_wm_take_focus = atoms_return[5];
+  motif_wm_hints = atoms_return[6];
+
+  _ewmh = new bt::EWMH(display());
+}
+
+
+void Blackbox::updateActiveWindow() const {
+  Window active = (focused_window) ? focused_window->clientWindow() : None;
+  for (unsigned int i = 0; i < display().screenCount(); ++i)
+    _ewmh->setActiveWindow(display().screenInfo(i).rootWindow(), active);
+}
+
+
+void Blackbox::shutdown(void) {
+  bt::Application::shutdown();
+
+  XGrabServer();
+
+  XSetInputFocus(XDisplay(), PointerRoot, RevertToPointerRoot, XTime());
+
+  std::for_each(screen_list, screen_list + screen_list_count,
+                std::mem_fun(&BScreen::shutdown));
+
+  XSync(XDisplay(), false);
+
+  XUngrabServer();
+}
+
+
+static Bool scanForFocusIn(Display *, XEvent *e, XPointer) {
+  if (e->type == FocusIn
+      && e->xfocus.mode != NotifyGrab
+      && (e->xfocus.detail == NotifyNonlinearVirtual
+          || e->xfocus.detail == NotifyVirtual)) {
+    return true;
+  }
+  return false;
+}
+
+
+void Blackbox::process_event(XEvent *e) {
+  switch (e->type) {
+  case MapRequest: {
+#ifdef    DEBUG
+    fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
+            e->xmaprequest.window);
+#endif // DEBUG
+
+    BlackboxWindow *win = findWindow(e->xmaprequest.window);
+
+    if (win) {
+      if ((!activeScreen() || activeScreen() == win->screen()) &&
+          (win->isTransient() || _resource.focusNewWindows()))
+        win->activate();
+    } else {
+      BScreen *screen = findScreen(e->xmaprequest.parent);
+
+      if (! screen) {
+        /*
+          we got a map request for a window who's parent isn't root. this
+          can happen in only one circumstance:
+
+          a client window unmapped a managed window, and then remapped it
+          somewhere between unmapping the client window and reparenting it
+          to root.
+
+          regardless of how it happens, we need to find the screen that
+          the window is on
+        */
+        XWindowAttributes wattrib;
+        if (! XGetWindowAttributes(XDisplay(), e->xmaprequest.window,
+                                   &wattrib)) {
+          // failed to get the window attributes, perhaps the window has
+          // now been destroyed?
+          break;
+        }
+
+        screen = findScreen(wattrib.root);
+        assert(screen != 0); // this should never happen
+      }
+      screen->addWindow(e->xmaprequest.window);
+    }
+
+    break;
+  }
+
+  case ConfigureRequest: {
+    BlackboxWindow *win = findWindow(e->xconfigurerequest.window);
+    if (win) {
+      // a window wants to resize
+      win->configureRequestEvent(&e->xconfigurerequest);
+      break;
+    }
+
+    Slit *slit =
+      dynamic_cast<Slit *>(findEventHandler(e->xconfigurerequest.parent));
+    if (slit) {
+      // something in the slit wants to resize
+      slit->configureRequestEvent(&e->xconfigurerequest);
+      break;
+    }
+
+    /*
+      handle configure requests for windows that have no EventHandlers
+      by simply configuring them as requested.
+
+      note: the event->window parameter points to the window being
+      configured, and event->parent points to the window that received
+      the event (in this case, the root window, since
+      SubstructureRedirect has been selected).
+    */
+    XWindowChanges xwc;
+    xwc.x = e->xconfigurerequest.x;
+    xwc.y = e->xconfigurerequest.y;
+    xwc.width = e->xconfigurerequest.width;
+    xwc.height = e->xconfigurerequest.height;
+    xwc.border_width = e->xconfigurerequest.border_width;
+    xwc.sibling = e->xconfigurerequest.above;
+    xwc.stack_mode = e->xconfigurerequest.detail;
+    XConfigureWindow(XDisplay(),
+                     e->xconfigurerequest.window,
+                     e->xconfigurerequest.value_mask,
+                     &xwc);
+    break;
+  }
+
+  case FocusIn: {
+#ifdef FOCUS_DEBUG
+    printf("FocusIn : window %8lx mode %s detail %s\n",
+           e->xfocus.window, Mode[e->xfocus.mode], Detail[e->xfocus.detail]);
+#endif
+
+    if (e->xfocus.mode == NotifyGrab
+        || (e->xfocus.detail != NotifyNonlinearVirtual
+            && e->xfocus.detail != NotifyVirtual)) {
+      /*
+        don't process FocusIns when:
+        1. they are the result of a grab
+        2. the new focus window isn't an ancestor or inferior of the
+        old focus window (NotifyNonlinearVirtual and NotifyVirtual)
+      */
+      break;
+    }
+
+    BlackboxWindow *win = findWindow(e->xfocus.window);
+    if (!win || win->isFocused())
+      break;
+
+#ifdef FOCUS_DEBUG
+    printf("          win %p got focus\n", win);
+#endif
+    win->setFocused(true);
+    setFocusedWindow(win);
+
+    /*
+      set the event window to None.  when the FocusOut event handler calls
+      this function recursively, it uses this as an indication that focus
+      has moved to a known window.
+    */
+    e->xfocus.window = None;
+
+    break;
+  }
+
+  case FocusOut: {
+#ifdef FOCUS_DEBUG
+    printf("FocusOut: window %8lx mode %s detail %s\n",
+           e->xfocus.window, Mode[e->xfocus.mode], Detail[e->xfocus.detail]);
+#endif
+
+    if (e->xfocus.mode == NotifyGrab
+        || (e->xfocus.detail != NotifyNonlinearVirtual
+            && e->xfocus.detail != NotifyVirtual)) {
+      /*
+        don't process FocusOuts when:
+        1. they are the result of a grab
+        2. the new focus window isn't an ancestor or inferior of the
+        old focus window (NotifyNonlinearVirtual and NotifyNonlinearVirtual)
+      */
+      break;
+    }
+
+    BlackboxWindow *win = findWindow(e->xfocus.window);
+    if (!win || !win->isFocused())
+      break;
+
+    bool lost_focus = true; // did the window really lose focus?
+    bool no_focus = true;   // did another window get focus?
+
+    XEvent event;
+    if (XCheckIfEvent(XDisplay(), &event, scanForFocusIn, NULL)) {
+      process_event(&event);
+
+      if (event.xfocus.window == None)
+        no_focus = false;
+    } else {
+      XWindowAttributes attr;
+      Window w;
+      int unused;
+      XGetInputFocus(XDisplay(), &w, &unused);
+      if (w != None
+          && XGetWindowAttributes(XDisplay(), w, &attr)
+          && attr.override_redirect) {
+#ifdef FOCUS_DEBUG
+        printf("          focused moved to an override_redirect window\n");
+#endif
+        lost_focus = (e->xfocus.mode == NotifyNormal);
+      }
+    }
+
+    if (lost_focus) {
+#ifdef FOCUS_DEBUG
+      printf("          win %p lost focus\n", win);
+#endif
+      win->setFocused(false);
+
+      if (no_focus) {
+#ifdef FOCUS_DEBUG
+        printf("          no window has focus\n");
+#endif
+        setFocusedWindow(0);
+      }
+    }
+
+    break;
+  }
+
+  default:
+    // Send the event through the default EventHandlers.
+    bt::Application::process_event(e);
+    break;
+  } // switch
+}
+
+
+bool Blackbox::process_signal(int sig) {
+  switch (sig) {
+  case SIGHUP:
+    reconfigure();
+    break;
+
+  case SIGUSR1:
+    reload_rc();
+    break;
+
+  case SIGUSR2:
+    rereadMenu();
+    break;
+
+  default:
+    return bt::Application::process_signal(sig);
+  } // switch
+
+  return true;
+}
+
+
+void Blackbox::timeout(bt::Timer *) {
+  XrmDatabase new_blackboxrc = (XrmDatabase) 0;
+
+  std::string style = "session.styleFile: ";
+  style += _resource.styleFilename();
+  XrmPutLineResource(&new_blackboxrc, style.c_str());
+
+  XrmDatabase old_blackboxrc = XrmGetFileDatabase(_resource.rcFilename());
+
+  XrmMergeDatabases(new_blackboxrc, &old_blackboxrc);
+  XrmPutFileDatabase(old_blackboxrc, _resource.rcFilename());
+  if (old_blackboxrc) XrmDestroyDatabase(old_blackboxrc);
+
+  std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
+                bt::PointerAssassin());
+  menuTimestamps.clear();
+
+  std::for_each(screen_list, screen_list + screen_list_count,
+                std::mem_fun(&BScreen::reconfigure));
+
+  bt::Font::clearCache();
+  bt::PixmapCache::clearCache();
+  bt::Pen::clearCache();
+
+  // clear the color cache here to allow the pen cache to deallocate
+  // all unused colors
+  bt::Color::clearCache();
+}
+
+
+Blackbox::Blackbox(char **m_argv, const char *dpy_name,
+                   const std::string& rc, bool multi_head)
+  : bt::Application(m_argv[0], dpy_name, multi_head),
+    grab_count(0u), _resource(rc)
+{
+  if (! XSupportsLocale())
+    fprintf(stderr, "X server does not support locale\n");
+
+  if (XSetLocaleModifiers("") == NULL)
+    fprintf(stderr, "cannot set locale modifiers\n");
+
+  argv = m_argv;
+
+  active_screen = 0;
+  focused_window = (BlackboxWindow *) 0;
+  _ewmh = (bt::EWMH*) 0;
+
+  init_icccm();
+
+  if (! multi_head || display().screenCount() == 1)
+    screen_list_count = 1;
+  else
+    screen_list_count = display().screenCount();
+
+  _resource.load(*this);
+
+  screen_list = new BScreen*[screen_list_count];
+  unsigned int managed = 0;
+  for (unsigned int i = 0; i < screen_list_count; ++i) {
+    BScreen *screen = new BScreen(this, i);
+
+    if (! screen->isScreenManaged()) {
+      delete screen;
+      continue;
+    }
+
+    screen_list[i] = screen;
+    ++managed;
+  }
+
+  if (managed == 0) {
+    fprintf(stderr, "%s: no managable screens found, exiting...\n",
+            applicationName().c_str());
+    ::exit(3);
+  }
+
+  screen_list_count = managed;
+
+  // start with the first managed screen as the active screen
+  setActiveScreen(screen_list[0]);
+
+  XSynchronize(XDisplay(), false);
+  XSync(XDisplay(), false);
+
+  timer = new bt::Timer(this, this);
+  timer->setTimeout(0l);
+}
+
+
+Blackbox::~Blackbox(void) {
+  std::for_each(screen_list, screen_list + screen_list_count,
+                bt::PointerAssassin());
+
+  delete [] screen_list;
+  std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
+                bt::PointerAssassin());
+
+  delete timer;
+  delete _ewmh;
+}
+
+
+void Blackbox::XGrabServer(void) {
+  if (grab_count++ == 0)
+    ::XGrabServer(XDisplay());
+}
+
+
+void Blackbox::XUngrabServer(void) {
+  if (--grab_count == 0)
+    ::XUngrabServer(XDisplay());
+}
+
+
+BScreen *Blackbox::findScreen(Window window) const {
+  for (unsigned int i = 0; i < screen_list_count; ++i)
+    if (screen_list[i]->screenInfo().rootWindow() == window)
+      return screen_list[i];
+  return 0;
+}
+
+
+void Blackbox::setActiveScreen(BScreen *screen) {
+  if (active_screen && active_screen == screen) // nothing to do
+    return;
+
+  assert(screen != 0);
+  active_screen = screen;
+
+  // install screen colormap
+  XInstallColormap(XDisplay(), active_screen->screenInfo().colormap());
+
+  if (! focused_window || focused_window->screen() != active_screen)
+    setFocusedWindow(0);
+}
+
+
+BScreen* Blackbox::screenNumber(unsigned int n) {
+  assert(n < screen_list_count);
+  return screen_list[n];
+}
+
+
+BlackboxWindow *Blackbox::findWindow(Window window) const {
+  WindowLookup::const_iterator it = windowSearchList.find(window);
+  if (it != windowSearchList.end())
+    return it->second;
+  return 0;
+}
+
+
+void Blackbox::insertWindow(Window window, BlackboxWindow *data)
+{ windowSearchList.insert(WindowLookupPair(window, data)); }
+
+
+void Blackbox::removeWindow(Window window)
+{ windowSearchList.erase(window); }
+
+
+BWindowGroup *Blackbox::findWindowGroup(Window window) const {
+  GroupLookup::const_iterator it = groupSearchList.find(window);
+  if (it != groupSearchList.end())
+    return it->second;
+  return 0;
+}
+
+
+void Blackbox::insertWindowGroup(Window window, BWindowGroup *data)
+{ groupSearchList.insert(GroupLookupPair(window, data)); }
+
+
+void Blackbox::removeWindowGroup(Window window)
+{ groupSearchList.erase(window); }
+
+
+void Blackbox::setFocusedWindow(BlackboxWindow *win) {
+  if (focused_window && focused_window == win) // nothing to do
+    return;
+
+  if (win && !win->isIconic()) {
+    // the active screen is the one with the newly focused window...
+    active_screen = win->screen();
+    focused_window = win;
+  } else {
+    // nothing has focus
+    focused_window = 0;
+    assert(active_screen != 0);
+    XSetInputFocus(XDisplay(), active_screen->noFocusWindow(),
+                   RevertToPointerRoot, XTime());
+  }
+
+  updateActiveWindow();
+}
+
+
+void Blackbox::restart(const std::string &prog) {
+  setRunState(bt::Application::SHUTDOWN);
+
+  /*
+    since we don't allow control to return to the eventloop, we need
+    to call shutdown() explicitly
+  */
+  shutdown();
+
+  if (! prog.empty()) {
+    putenv(const_cast<char *>
+           (display().screenInfo(0).displayString().c_str()));
+    execlp(prog.c_str(), prog.c_str(), NULL);
+    perror(prog.c_str());
+  }
+
+  // fall back in case the above execlp doesn't work
+  execvp(argv[0], argv);
+  std::string name = bt::basename(argv[0]);
+  execvp(name.c_str(), argv);
+}
+
+
+void Blackbox::reconfigure(void) {
+  if (! timer->isTiming())
+    timer->start();
+}
+
+
+void Blackbox::saveMenuFilename(const std::string& filename) {
+  assert(!filename.empty());
+  bool found = false;
+
+  MenuTimestampList::iterator it = menuTimestamps.begin();
+  for (; it != menuTimestamps.end() && !found; ++it)
+    found = (*it)->filename == filename;
+  if (found)
+    return;
+
+  struct stat buf;
+  if (stat(filename.c_str(), &buf) != 0)
+    return; // file doesn't exist
+
+  MenuTimestamp *ts = new MenuTimestamp;
+  ts->filename = filename;
+  ts->timestamp = buf.st_ctime;
+  menuTimestamps.push_back(ts);
+}
+
+
+void Blackbox::checkMenu(void) {
+  bool reread = false;
+  MenuTimestampList::iterator it = menuTimestamps.begin();
+  for(; it != menuTimestamps.end(); ++it) {
+    MenuTimestamp *tmp = *it;
+    struct stat buf;
+
+    if (! stat(tmp->filename.c_str(), &buf)) {
+      if (tmp->timestamp != buf.st_ctime)
+        reread = true;
+    } else {
+      reread = true;
+    }
+  }
+
+  if (reread)
+    rereadMenu();
+}
+
+
+void Blackbox::rereadMenu(void) {
+  std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
+                bt::PointerAssassin());
+  menuTimestamps.clear();
+
+  std::for_each(screen_list, screen_list + screen_list_count,
+                std::mem_fun(&BScreen::rereadMenu));
+}
diff --git a/.pc/070_fix-ftbfs-x32.patch/src/Toolbar.cc b/.pc/070_fix-ftbfs-x32.patch/src/Toolbar.cc
new file mode 100644
index 0000000..e88b8f6
--- /dev/null
+++ b/.pc/070_fix-ftbfs-x32.patch/src/Toolbar.cc
@@ -0,0 +1,811 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Toolbar.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "Toolbar.hh"
+#include "Iconmenu.hh"
+#include "Screen.hh"
+#include "Slit.hh"
+#include "Toolbarmenu.hh"
+#include "Window.hh"
+#include "Windowmenu.hh"
+#include "Workspacemenu.hh"
+
+#include <Pen.hh>
+#include <PixmapCache.hh>
+#include <Unicode.hh>
+
+#include <X11/Xutil.h>
+#include <sys/time.h>
+#include <assert.h>
+
+
+long nextTimeout(int resolution)
+{
+  timeval now;
+  gettimeofday(&now, 0);
+  return (std::max(1000l, ((((resolution - (now.tv_sec % resolution)) * 1000l))
+                           - (now.tv_usec / 1000l))));
+}
+
+
+Toolbar::Toolbar(BScreen *scrn) {
+  _screen = scrn;
+  blackbox = _screen->blackbox();
+
+  // get the clock updating every minute
+  clock_timer = new bt::Timer(blackbox, this);
+  clock_timer->recurring(True);
+
+  const ToolbarOptions &options = _screen->resource().toolbarOptions();
+
+  const std::string &time_format = options.strftime_format;
+  if (time_format.find("%S") != std::string::npos
+      || time_format.find("%s") != std::string::npos
+      || time_format.find("%r") != std::string::npos
+      || time_format.find("%T") != std::string::npos) {
+    clock_timer_resolution = 1;
+  } else if (time_format.find("%M") != std::string::npos
+             || time_format.find("%R") != std::string::npos) {
+    clock_timer_resolution = 60;
+  } else {
+    clock_timer_resolution = 3600;
+  }
+
+  hide_timer = new bt::Timer(blackbox, this);
+  hide_timer->setTimeout(blackbox->resource().autoRaiseDelay());
+
+  setLayer(options.always_on_top
+           ? StackingList::LayerAbove
+           : StackingList::LayerNormal);
+  hidden = options.auto_hide;
+
+  new_name_pos = 0;
+
+  display = blackbox->XDisplay();
+  XSetWindowAttributes attrib;
+  unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
+  attrib.colormap = _screen->screenInfo().colormap();
+  attrib.override_redirect = True;
+  attrib.event_mask = ButtonPressMask | ButtonReleaseMask |
+                      EnterWindowMask | LeaveWindowMask | ExposureMask;
+
+  frame.window =
+    XCreateWindow(display, _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.window, this);
+
+  attrib.event_mask =
+    ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask;
+
+  frame.workspace_label =
+    XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.workspace_label, this);
+
+  frame.window_label =
+    XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.window_label, this);
+
+  frame.clock =
+    XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.clock, this);
+
+  frame.psbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.psbutton, this);
+
+  frame.nsbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.nsbutton, this);
+
+  frame.pwbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.pwbutton, this);
+
+  frame.nwbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.nwbutton, this);
+
+  frame.base = frame.slabel = frame.wlabel = frame.clk = frame.button =
+ frame.pbutton = None;
+
+  _screen->addStrut(&strut);
+
+  reconfigure();
+
+  clock_timer->setTimeout(nextTimeout(clock_timer_resolution));
+  clock_timer->start();
+
+  XMapSubwindows(display, frame.window);
+  XMapWindow(display, frame.window);
+}
+
+
+Toolbar::~Toolbar(void) {
+  _screen->removeStrut(&strut);
+
+  XUnmapWindow(display, frame.window);
+
+  bt::PixmapCache::release(frame.base);
+  bt::PixmapCache::release(frame.slabel);
+  bt::PixmapCache::release(frame.wlabel);
+  bt::PixmapCache::release(frame.clk);
+  bt::PixmapCache::release(frame.button);
+  bt::PixmapCache::release(frame.pbutton);
+
+  blackbox->removeEventHandler(frame.window);
+  blackbox->removeEventHandler(frame.workspace_label);
+  blackbox->removeEventHandler(frame.window_label);
+  blackbox->removeEventHandler(frame.clock);
+  blackbox->removeEventHandler(frame.psbutton);
+  blackbox->removeEventHandler(frame.nsbutton);
+  blackbox->removeEventHandler(frame.pwbutton);
+  blackbox->removeEventHandler(frame.nwbutton);
+
+  // all children windows are destroyed by this call as well
+  XDestroyWindow(display, frame.window);
+
+  delete hide_timer;
+  delete clock_timer;
+}
+
+
+unsigned int Toolbar::exposedHeight(void) const {
+  const ToolbarOptions &options = _screen->resource().toolbarOptions();
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+  return (options.auto_hide ? style.hidden_height : style.toolbar_height);
+}
+
+
+void Toolbar::reconfigure(void) {
+  ScreenResource &resource = _screen->resource();
+  const ToolbarOptions &options = resource.toolbarOptions();
+  const ToolbarStyle &style = resource.toolbarStyle();
+
+  unsigned int width = (_screen->screenInfo().width() *
+                        options.width_percent) / 100;
+
+  const unsigned int border_width = style.toolbar.borderWidth();
+  const unsigned int extra =
+    style.frame_margin == 0 ? style.button.borderWidth() : 0;
+  frame.rect.setSize(width, style.toolbar_height);
+
+  int x, y;
+  switch (options.placement) {
+  case TopLeft:
+  case TopRight:
+  case TopCenter:
+    switch (options.placement) {
+    case TopLeft:
+      x = 0;
+      break;
+    case TopRight:
+      x = _screen->screenInfo().width() - frame.rect.width();
+      break;
+    default:
+      x = (_screen->screenInfo().width() - frame.rect.width()) / 2;
+      break;
+    }
+    y = 0;
+    frame.y_hidden = style.hidden_height - frame.rect.height();
+    break;
+
+  case BottomLeft:
+  case BottomRight:
+  case BottomCenter:
+  default:
+    switch (options.placement) {
+    case BottomLeft:
+      x = 0;
+      break;
+    case BottomRight:
+      x = _screen->screenInfo().width() - frame.rect.width();
+      break;
+    default:
+      x = (_screen->screenInfo().width() - frame.rect.width()) / 2;
+      break;
+    }
+    y = _screen->screenInfo().height() - frame.rect.height();
+    frame.y_hidden = _screen->screenInfo().height() - style.hidden_height;
+    break;
+  }
+
+  frame.rect.setPos(x, y);
+
+  updateStrut();
+
+  time_t ttmp = time(NULL);
+
+  unsigned int clock_w = 0u, label_w = 0u;
+
+  if (ttmp != -1) {
+    struct tm *tt = localtime(&ttmp);
+    if (tt) {
+      char t[1024];
+      int len = strftime(t, 1024, options.strftime_format.c_str(), tt);
+      if (len == 0) { // invalid time format found
+        // so use the default
+        const_cast<std::string &>(options.strftime_format) = "%I:%M %p";
+        len = strftime(t, 1024, options.strftime_format.c_str(), tt);
+      }
+      /*
+       * find the length of the rendered string and add room for two extra
+       * characters to it.  This allows for variable width output of the fonts.
+       * two 'w' are used to get the widest possible width
+       */
+      clock_w =
+        bt::textRect(_screen->screenNumber(), style.font,
+                     bt::toUnicode(t)).width() +
+        bt::textRect(_screen->screenNumber(), style.font,
+                     bt::toUnicode("ww")).width();
+    }
+  }
+
+  for (unsigned int i = 0; i < _screen->workspaceCount(); i++) {
+    width =
+      bt::textRect(_screen->screenNumber(), style.font,
+                   _screen->resource().workspaceName(i)).width();
+    label_w = std::max(label_w, width);
+  }
+
+  label_w = clock_w = std::max(label_w, clock_w) + (style.label_margin * 2);
+
+  unsigned int window_label_w =
+    (frame.rect.width() - (border_width * 2)
+     - (clock_w + (style.button_width * 4) + label_w
+        + (style.frame_margin * 8))
+     + extra*6);
+
+  XMoveResizeWindow(display, frame.window, frame.rect.x(),
+                    hidden ? frame.y_hidden : frame.rect.y(),
+                    frame.rect.width(), frame.rect.height());
+
+  // workspace label
+  frame.slabel_rect.setRect(border_width + style.frame_margin,
+                            border_width + style.frame_margin,
+                            label_w,
+                            style.label_height);
+  // previous workspace button
+  frame.ps_rect.setRect(border_width + (style.frame_margin * 2) + label_w
+                        - extra,
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // next workspace button
+  frame.ns_rect.setRect(border_width + (style.frame_margin * 3)
+                        + label_w + style.button_width - (extra * 2),
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // window label
+  frame.wlabel_rect.setRect(border_width + (style.frame_margin * 4)
+                            + (style.button_width * 2) + label_w - (extra * 3),
+                            border_width + style.frame_margin,
+                            window_label_w,
+                            style.label_height);
+  // previous window button
+  frame.pw_rect.setRect(border_width + (style.frame_margin * 5)
+                        + (style.button_width * 2) + label_w
+                        + window_label_w - (extra * 4),
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // next window button
+  frame.nw_rect.setRect(border_width + (style.frame_margin * 6)
+                        + (style.button_width * 3) + label_w
+                        + window_label_w - (extra * 5),
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // clock
+  frame.clock_rect.setRect(frame.rect.width() - clock_w - style.frame_margin
+                           - border_width,
+                           border_width + style.frame_margin,
+                           clock_w,
+                           style.label_height);
+
+  XMoveResizeWindow(display, frame.workspace_label,
+                    frame.slabel_rect.x(), frame.slabel_rect.y(),
+                    frame.slabel_rect.width(), frame.slabel_rect.height());
+  XMoveResizeWindow(display, frame.psbutton,
+                    frame.ps_rect.x(), frame.ps_rect.y(),
+                    frame.ps_rect.width(), frame.ps_rect.height());
+  XMoveResizeWindow(display, frame.nsbutton,
+                    frame.ns_rect.x(), frame.ns_rect.y(),
+                    frame.ns_rect.width(), frame.ns_rect.height());
+  XMoveResizeWindow(display, frame.window_label,
+                    frame.wlabel_rect.x(), frame.wlabel_rect.y(),
+                    frame.wlabel_rect.width(), frame.wlabel_rect.height());
+  XMoveResizeWindow(display, frame.pwbutton,
+                    frame.pw_rect.x(), frame.pw_rect.y(),
+                    frame.pw_rect.width(), frame.pw_rect.height());
+  XMoveResizeWindow(display, frame.nwbutton,
+                    frame.nw_rect.x(), frame.nw_rect.y(),
+                    frame.nw_rect.width(), frame.nw_rect.height());
+  XMoveResizeWindow(display, frame.clock,
+                    frame.clock_rect.x(), frame.clock_rect.y(),
+                    frame.clock_rect.width(), frame.clock_rect.height());
+
+  frame.base =
+    bt::PixmapCache::find(_screen->screenNumber(), style.toolbar,
+                          frame.rect.width(), frame.rect.height(),
+                          frame.base);
+  frame.slabel =
+    bt::PixmapCache::find(_screen->screenNumber(), style.slabel,
+                          frame.slabel_rect.width(),
+                          frame.slabel_rect.height(),
+                          frame.slabel);
+  frame.wlabel =
+    bt::PixmapCache::find(_screen->screenNumber(), style.wlabel,
+                          frame.wlabel_rect.width(),
+                          frame.wlabel_rect.height(),
+                          frame.wlabel);
+  frame.clk =
+    bt::PixmapCache::find(_screen->screenNumber(), style.clock,
+                          frame.clock_rect.width(),
+                          frame.clock_rect.height(),
+                          frame.clk);
+  frame.button =
+    bt::PixmapCache::find(_screen->screenNumber(), style.button,
+                          style.button_width, style.button_width,
+                          frame.button);
+  frame.pbutton =
+    bt::PixmapCache::find(_screen->screenNumber(),
+                          style.pressed,
+                          style.button_width, style.button_width,
+                          frame.pbutton);
+
+  XClearArea(display, frame.window, 0, 0,
+             frame.rect.width(), frame.rect.height(), True);
+
+  XClearArea(display, frame.workspace_label, 0, 0,
+             label_w, style.label_height, True);
+  XClearArea(display, frame.window_label, 0, 0,
+             window_label_w, style.label_height, True);
+  XClearArea(display, frame.clock, 0, 0,
+             clock_w, style.label_height, True);
+
+  XClearArea(display, frame.psbutton, 0, 0,
+             style.button_width, style.button_width, True);
+  XClearArea(display, frame.nsbutton, 0, 0,
+             style.button_width, style.button_width, True);
+  XClearArea(display, frame.pwbutton, 0, 0,
+             style.button_width, style.button_width, True);
+  XClearArea(display, frame.nwbutton, 0, 0,
+             style.button_width, style.button_width, True);
+}
+
+
+void Toolbar::updateStrut(void) {
+  // left and right are always 0
+  strut.top = strut.bottom = 0;
+
+  switch(_screen->resource().toolbarOptions().placement) {
+  case TopLeft:
+  case TopCenter:
+  case TopRight:
+    strut.top = exposedHeight();
+    break;
+  default:
+    strut.bottom = exposedHeight();
+  }
+
+  _screen->updateStrut();
+}
+
+
+void Toolbar::redrawClockLabel(void) {
+  const ToolbarOptions &options = _screen->resource().toolbarOptions();
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+
+  bt::Rect u(0, 0, frame.clock_rect.width(), frame.clock_rect.height());
+  if (frame.clk == ParentRelative) {
+    bt::Rect t(-frame.clock_rect.x(), -frame.clock_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.clock, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(), style.clock,
+                    frame.clock, u, u, frame.clk);
+  }
+
+  time_t tmp = 0;
+  struct tm *tt = 0;
+  char str[1024];
+  if ((tmp = time(NULL)) == -1)
+    return; // should not happen
+  tt = localtime(&tmp);
+  if (! tt)
+    return; // ditto
+  if (! strftime(str, sizeof(str), options.strftime_format.c_str(), tt))
+    return; // ditto
+
+  bt::Pen pen(_screen->screenNumber(), style.clock_text);
+  bt::drawText(style.font, pen, frame.clock, u, style.alignment,
+               bt::toUnicode(str));
+}
+
+
+void Toolbar::redrawWindowLabel(void) {
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+
+  bt::Rect u(0, 0, frame.wlabel_rect.width(), frame.wlabel_rect.height());
+  if (frame.wlabel == ParentRelative) {
+    bt::Rect t(-frame.wlabel_rect.x(), -frame.wlabel_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.window_label, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(), style.wlabel,
+                    frame.window_label, u, u, frame.wlabel);
+  }
+
+  BlackboxWindow *foc = _screen->blackbox()->focusedWindow();
+  if (! foc || foc->screen() != _screen)
+    return;
+
+  bt::Pen pen(_screen->screenNumber(), style.wlabel_text);
+  bt::drawText(style.font, pen, frame.window_label, u,
+               style.alignment,
+               bt::ellideText(foc->title(), u.width(), bt::toUnicode("..."),
+                              _screen->screenNumber(), style.font));
+}
+
+
+void Toolbar::redrawWorkspaceLabel(void) {
+  const bt::ustring &name =
+    _screen->resource().workspaceName(_screen->currentWorkspace());
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+
+  bt::Rect u(0, 0, frame.slabel_rect.width(), frame.slabel_rect.height());
+  if (frame.slabel == ParentRelative) {
+    bt::Rect t(-frame.slabel_rect.x(), -frame.slabel_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.workspace_label, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(), style.slabel,
+                    frame.workspace_label, u, u, frame.slabel);
+  }
+
+  bt::Pen pen(_screen->screenNumber(), style.slabel_text);
+  bt::drawText(style.font, pen, frame.workspace_label, u,
+               style.alignment, name);
+}
+
+
+void Toolbar::redrawPrevWorkspaceButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.ps_rect.x(), -frame.ps_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.psbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.psbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::leftArrow(_screen->screenNumber()),
+                 pen, frame.psbutton, u);
+}
+
+
+void Toolbar::redrawNextWorkspaceButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.ns_rect.x(), -frame.ns_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.nsbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.nsbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::rightArrow(_screen->screenNumber()),
+                 pen, frame.nsbutton, u);
+}
+
+
+void Toolbar::redrawPrevWindowButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.pw_rect.x(), -frame.pw_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.pwbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.pwbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::leftArrow(_screen->screenNumber()),
+                 pen, frame.pwbutton, u);
+}
+
+
+void Toolbar::redrawNextWindowButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.nw_rect.x(), -frame.nw_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.nwbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.nwbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::rightArrow(_screen->screenNumber()),
+                 pen, frame.nwbutton, u);
+}
+
+
+void Toolbar::buttonPressEvent(const XButtonEvent * const event) {
+  if (event->state == Mod1Mask) {
+    if (event->button == 1)
+      _screen->raiseWindow(this);
+    else if (event->button == 2)
+      _screen->lowerWindow(this);
+    return;
+  }
+
+  if (event->button == 1) {
+    if (event->window == frame.psbutton)
+      redrawPrevWorkspaceButton(True);
+    else if (event->window == frame.nsbutton)
+      redrawNextWorkspaceButton(True);
+    else if (event->window == frame.pwbutton)
+      redrawPrevWindowButton(True);
+    else if (event->window == frame.nwbutton)
+      redrawNextWindowButton(True);
+    return;
+  }
+
+  if (event->window == frame.window_label) {
+    BlackboxWindow *focus = blackbox->focusedWindow();
+    if (focus && focus->screen() == _screen) {
+      if (event->button == 2) {
+        _screen->lowerWindow(focus);
+      } else if (event->button == 3) {
+        Windowmenu *windowmenu = _screen->windowmenu(focus);
+        windowmenu->popup(event->x_root, event->y_root,
+                          _screen->availableArea());
+      } else if (blackbox->resource().toolbarActionsWithMouseWheel() &&
+                 blackbox->resource().shadeWindowWithMouseWheel() &&
+                 focus->hasWindowFunction(WindowFunctionShade)) {
+        if ((event->button == 4) && !focus->isShaded()) {
+          focus->setShaded(true);
+		  } else if ((event->button == 5) && focus->isShaded()) {
+          focus->setShaded(false);
+        }
+      }
+    }
+    return;
+  }
+
+  if ((event->window == frame.pwbutton) || (event->window == frame.nwbutton)) {
+    if (blackbox->resource().toolbarActionsWithMouseWheel()) {
+      if (event->button == 4)
+        _screen->nextFocus();
+      else if (event->button == 5)
+        _screen->prevFocus();
+    }
+    // XXX: current-workspace window-list menu with button 2 or 3 here..
+    return;
+  }
+
+  // XXX: workspace-menu with button 2 or 3 on workspace-items (prev/next/label) here
+
+  // default-handlers (scroll through workspaces, popup toolbar-menu)
+  if (event->button == 3) {
+    _screen->toolbarmenu()->popup(event->x_root, event->y_root,
+                                  _screen->availableArea());
+    return;
+  }
+
+  if (blackbox->resource().toolbarActionsWithMouseWheel()) {
+    if (event->button == 4)
+      _screen->nextWorkspace();
+    else if (event->button == 5)
+      _screen->prevWorkspace();
+  }
+}
+
+
+void Toolbar::buttonReleaseEvent(const XButtonEvent * const event) {
+  if (event->button == 1) {
+    const ToolbarStyle &style =
+      _screen->resource().toolbarStyle();
+
+    if (event->window == frame.psbutton) {
+      redrawPrevWorkspaceButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width)) {
+        _screen->prevWorkspace();
+      }
+    } else if (event->window == frame.nsbutton) {
+      redrawNextWorkspaceButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width)) {
+        _screen->nextWorkspace();
+      }
+    } else if (event->window == frame.pwbutton) {
+      redrawPrevWindowButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width))
+        _screen->prevFocus();
+    } else if (event->window == frame.nwbutton) {
+      redrawNextWindowButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width))
+        _screen->nextFocus();
+    } else if (event->window == frame.window_label)
+      _screen->raiseFocus();
+  }
+}
+
+
+void Toolbar::enterNotifyEvent(const XCrossingEvent * const /*unused*/) {
+  if (!_screen->resource().toolbarOptions().auto_hide)
+    return;
+
+  if (hidden) {
+    if (! hide_timer->isTiming())
+      hide_timer->start();
+  } else if (hide_timer->isTiming()) {
+    hide_timer->stop();
+  }
+}
+
+void Toolbar::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
+  if (!_screen->resource().toolbarOptions().auto_hide)
+    return;
+
+  if (hidden) {
+    if (hide_timer->isTiming())
+      hide_timer->stop();
+  } else if (! hide_timer->isTiming()) {
+    hide_timer->start();
+  }
+}
+
+
+void Toolbar::exposeEvent(const XExposeEvent * const event) {
+  if (event->window == frame.clock) redrawClockLabel();
+  else if (event->window == frame.workspace_label) redrawWorkspaceLabel();
+  else if (event->window == frame.window_label) redrawWindowLabel();
+  else if (event->window == frame.psbutton) redrawPrevWorkspaceButton();
+  else if (event->window == frame.nsbutton) redrawNextWorkspaceButton();
+  else if (event->window == frame.pwbutton) redrawPrevWindowButton();
+  else if (event->window == frame.nwbutton) redrawNextWindowButton();
+  else if (event->window == frame.window) {
+    bt::Rect t(0, 0, frame.rect.width(), frame.rect.height());
+    bt::Rect r(event->x, event->y, event->width, event->height);
+    bt::drawTexture(_screen->screenNumber(),
+                    _screen->resource().toolbarStyle().toolbar,
+                    frame.window, t, r & t, frame.base);
+  }
+}
+
+
+void Toolbar::timeout(bt::Timer *timer) {
+  if (timer == clock_timer) {
+    redrawClockLabel();
+
+    clock_timer->setTimeout(nextTimeout(clock_timer_resolution));
+  } else if (timer == hide_timer) {
+    hidden = ! hidden;
+    if (hidden)
+      XMoveWindow(display, frame.window,
+                  frame.rect.x(), frame.y_hidden);
+    else
+      XMoveWindow(display, frame.window,
+                  frame.rect.x(), frame.rect.y());
+  } else {
+    // this should not happen
+    assert(0);
+  }
+}
+
+
+void Toolbar::toggleAutoHide(void) {
+  bool do_auto_hide = !_screen->resource().toolbarOptions().auto_hide;
+
+  updateStrut();
+  if (_screen->slit())
+    _screen->slit()->reposition();
+
+  if (!do_auto_hide && hidden) {
+    // force the toolbar to be visible
+    if (hide_timer->isTiming()) hide_timer->stop();
+    hide_timer->fireTimeout();
+  }
+}
+
+
+void Toolbar::setPlacement(Placement place) {
+  ToolbarOptions &options =
+    const_cast<ToolbarOptions &>(_screen->resource().toolbarOptions());
+  options.placement = place;
+  reconfigure();
+
+  // reposition the slit as well to make sure it doesn't intersect the
+  // toolbar
+  if (_screen->slit())
+    _screen->slit()->reposition();
+
+  _screen->saveResource();
+}
diff --git a/.pc/080_configure.ac.patch/configure.ac b/.pc/080_configure.ac.patch/configure.ac
new file mode 100644
index 0000000..5eda4c2
--- /dev/null
+++ b/.pc/080_configure.ac.patch/configure.ac
@@ -0,0 +1,329 @@
+dnl configure.in for Blackbox - an X11 Window manager
+dnl Initialize autoconf and automake
+
+AC_INIT([blackbox], [0.70.1], [http://blackboxwm.sourceforge.net])
+AM_INIT_AUTOMAKE([blackbox], [0.70.1])
+AC_CONFIG_SRCDIR([src/blackbox.cc])
+
+dnl Determine default prefix
+test "x$prefix" = "xNONE" && prefix="$ac_default_prefix"
+
+dnl Look in the most logical places for external libraries
+CPPFLAGS="$CPPFLAGS -I$prefix/include"
+LDFLAGS="$LDFLAGS -L$prefix/lib"
+if test "x$prefix" != "x/usr/local"; then
+  CPPFLAGS="$CPPFLAGS -I/usr/local/include"
+  LDFLAGS="$LDFLAGS -L/usr/local/lib"
+fi
+
+dnl Locate required external software
+AC_PROG_CC
+
+dnl Blackbox requires ANSI C headers
+AC_HEADER_STDC
+if test "$ac_cv_header_stdc" = "no"; then
+  AC_MSG_ERROR([Blackbox requires ANSI C headers.])
+fi
+
+AC_PROG_CXX
+AC_PROG_INSTALL
+
+dnl libbt shouldn't be shared by default (yet)
+AC_DISABLE_SHARED
+AC_PROG_LIBTOOL
+AC_SUBST(LIBTOOL_DEPS)
+
+AC_CHECK_PROGS([regex_cmd], [sed])
+if test "x$regex_cmd" = "x"; then
+  AC_MSG_ERROR([error. sed is required to build the default menu file.])
+fi
+
+AC_CHECK_PROGS([gencat_cmd], [gencat])
+if test "x$gencat_cmd" = "x"; then
+  NLS=
+fi
+
+dnl Blackbox uses C++
+AC_LANG([C++])
+
+dnl Compiler specific options
+if test "x$GXX" != "xyes"; then
+   mips_pro_ver=`$CC -version 2>&1 | grep -i mipspro | cut -f4 -d ' '`
+   if test "x$mips_pro_ver" != "x"; then
+      AC_MSG_CHECKING([for MIPSpro version])
+      AC_MSG_RESULT([$mips_pro_ver.])
+      AC_MSG_CHECKING(for -LANG:std in CXXFLAGS)
+      lang_std_not_set=`echo $CXXFLAGS | grep "\-LANG:std"`
+      if test "x$lang_std_not_set" = "x"; then
+         AC_MSG_RESULT([not set, setting.])
+         CXXFLAGS="${CXXFLAGS} -LANG:std"
+      else
+         AC_MSG_RESULT([already set.])
+      fi
+   fi 
+fi
+
+dnl Check if some required functions live in the C library, or in an
+dnl external library
+
+ICONV=
+AC_CHECK_FUNCS([iconv_open],
+               [],
+               [AC_CHECK_LIB([iconv],
+                             [iconv_open],
+                             [ICONV="-liconv"],
+                             [AC_CHECK_LIB([iconv],
+					   [libiconv_open],
+					   [ICONV="-liconv"],
+					   [AC_MSG_ERROR([Blackbox requires iconv(3) support.])])
+			     ])
+               ])
+AC_SUBST(ICONV)
+
+dnl check if we are using GNU libiconv
+AC_MSG_CHECKING([for GNU libiconv])
+AC_COMPILE_IFELSE([
+#include <sys/types.h>
+#include <iconv.h>
+
+int main(int, char **) {
+    iconv_t cd;
+    const char *inp;
+    char *outp;
+    size_t inbytes, outbytes;
+    iconv(cd, &inp, &inbytes, &outp, &outbytes);
+    return 0;
+}
+],
+ 		  [AC_DEFINE([HAVE_GNU_LIBICONV], [1],
+                             [Define to 1 when using GNU libiconv])
+		   AC_MSG_RESULT([yes])],
+		  [AC_MSG_RESULT([no, assuming POSIX compliance])]
+                 )
+
+LOCALE=
+AC_CHECK_FUNCS([setlocale],
+               [],
+               [AC_CHECK_LIB([xpg4],
+                             [setlocale],
+                             [LOCALE="-lxpg4"],
+                             [AC_MSG_ERROR([Blackbox requires setlocale(3) support.])])
+               ])
+AC_SUBST(LOCALE)
+
+AC_MSG_CHECKING([for nl_langinfo])
+AC_COMPILE_IFELSE([
+#include <langinfo.h>
+
+int main(int, char **)
+{
+    const char *x;
+    x = nl_langinfo(CODESET);
+    return 0;
+}
+],
+                  [AC_DEFINE([HAVE_NL_LANGINFO], [1],
+                             [Define to 1 if you system has nl_langinfo(3)])
+                   AC_MSG_RESULT([yes])],
+                  [AC_MSG_RESULT([no])]
+                 )
+
+dnl needed for some X11 libs
+AC_CHECK_LIB([nsl],
+             [t_open],
+	     [LIBS="$LIBS -lnsl"])
+AC_CHECK_LIB([socket],
+             [socket],
+	     [LIBS="$LIBS -lsocket"])
+
+dnl Check for X headers and libraries
+AC_PATH_X
+
+if test "x$no_x" = "xyes"; then
+  AC_MSG_ERROR([Blackbox requires the X Window System libraries and headers.])
+fi
+
+if test "x$x_includes" != "x"; then
+  CPPFLAGS="$CPPFLAGS -I$x_includes"
+  CXXFLAGS="$CXXFLAGS -I$x_includes"
+fi
+if test "x$x_libraries" != "x"; then
+  LIBS="$LIBS -L$x_libraries"
+fi
+
+dnl Check for required functions in -lX11
+XLIB=
+AC_CHECK_LIB([X11], [XOpenDisplay], [XLIB=yes], [XLIB=no])
+test "x$XLIB" = "xno" && AC_MSG_ERROR([standard X11 libraries not found])
+
+AC_CHECK_HEADERS([X11/Xlib.h], [XLIB=yes], [XLIB=no])
+test "x$XLIB" = "xno" && AC_MSG_ERROR([standard X11 headers not found])
+
+AC_CHECK_HEADERS([X11/Xatom.h X11/Xlocale.h X11/Xresource.h X11/Xutil.h X11/keysym.h], [XLIB=yes], [XLIB=no],
+[
+#include <X11/Xlib.h>
+])
+test "x$XLIB" = "xno" && AC_MSG_ERROR([standard X11 headers not found])
+
+dnl Check for SHAPE extension support and proper library files.
+AC_MSG_CHECKING([whether to build support for the SHAPE extension])
+AC_ARG_ENABLE([shape],
+              AC_HELP_STRING([--enable-shape],
+	                     [enable support of the SHAPE extension @<:@default=yes@:>@]),
+	      [SHAPE="$enableval"],
+	      [SHAPE=yes])
+AC_MSG_RESULT([$SHAPE])
+
+if test "x$SHAPE" = "xyes"; then
+  AC_CHECK_LIB([Xext], [XShapeCombineShape], [SHAPE=yes], [SHAPE=no])
+  
+  if test "x$SHAPE" = "xyes"; then
+    save_LIBS="$LIBS"
+      
+    if echo "$LIBS" | grep -q Xext 2>&1 >/dev/null; then
+      LIBS="$LIBS -lXext"
+    fi
+    AC_CHECK_HEADERS([X11/extensions/shape.h], [SHAPE=yes], [SHAPE=no],
+[
+#include <X11/Xlib.h>
+])
+      
+    if test "x$SHAPE" = "xyes"; then
+      SHAPE="-DSHAPE"
+    else
+      SHAPE=
+      LIBS="$save_LIBS"
+    fi
+  else
+    SHAPE=
+  fi
+else
+  SHAPE=
+fi
+AC_SUBST([SHAPE])
+
+dnl Check for MIT-SHM extension support and proper library files.
+AC_MSG_CHECKING([whether to build support for the MIT-SHM extension])
+AC_ARG_ENABLE([mitshm],
+              AC_HELP_STRING([--enable-mitshm],
+	                     [enable support of the MIT-SHM extension @<:@default=yes@:>@]),
+	      [MITSHM="$enableval"],
+	      [MITSHM=yes])
+AC_MSG_RESULT([$MITSHM])
+
+if test "x$MITSHM" = "xyes"; then
+  AC_CHECK_LIB([Xext], [XShmPutImage], [MITSHM=yes], [MITSHM=no])
+
+  if test "x$MITSHM" = "xyes"; then
+    save_LIBS="$LIBS"
+
+    LIBS="$LIBS -lXext"
+    AC_CHECK_HEADERS([X11/extensions/XShm.h], [MITSHM=yes], [MITSHM=no],
+[
+#include <X11/Xlib.h>
+])
+
+    if test "x$MITSHM" = "xyes"; then
+      MITSHM="-DMITSHM"
+    else
+      MITSHM=
+      LIBS="$save_LIBS"
+    fi
+  else
+    MITSHM=
+  fi
+else
+  MITSHM=
+fi
+AC_SUBST([MITSHM])
+
+LIBS="$LIBS -lX11"
+
+dnl Check for Xft libraries
+AC_MSG_CHECKING([whether to build support for Xft])
+AC_ARG_ENABLE([xft],
+              AC_HELP_STRING([--enable-xft],
+                             [enable support for the Xft library @<:@default=yes@:>@]),
+	      [XFT="$enableval"],
+	      [XFT=yes])
+AC_MSG_RESULT([$XFT])
+
+if test "x$XFT" = "xyes"; then
+  PKG_CHECK_MODULES([xft],
+                    [xft >= 2.0.0], 
+                    [XFT="-DXFT"
+                     XFT_PKGCONFIG="xft >= 2.0.0"
+                     CXXFLAGS="$CXXFLAGS $xft_CFLAGS"
+                     LIBS="$LIBS $xft_LIBS"],
+                    [XFT=
+                     XFT_PKGCONFIG=])
+else
+  XFT=
+  XFT_PKGCONFIG=
+fi
+AC_SUBST([XFT])
+AC_SUBST([XFT_PKGCONFIG])
+
+dnl Check whether to include debugging code
+AC_MSG_CHECKING([whether to include verbose debugging code])
+AC_ARG_ENABLE([debug],
+              AC_HELP_STRING([--enable-debug],
+	                     [include verbose debugging code @<:@default=no@:>@]),
+	      [DEBUG="$enableval"],
+	      [DEBUG=no])
+AC_MSG_RESULT([$DEBUG])
+
+if test "x$DEBUG" = "xyes"; then
+    DEBUG="-DDEBUG -fno-inline -g"
+else
+    DEBUG=
+fi
+AC_SUBST(DEBUG)
+
+dnl Check whether to include natural language support (i18n)
+AC_MSG_CHECKING([whether to include NLS support])
+AC_ARG_ENABLE([nls],
+              AC_HELP_STRING([--enable-nls],
+	                     [include natural language support @<:@default=yes@:>@]),
+              [NLS="$enableval"],
+	      [NLS=yes])
+AC_MSG_RESULT([$NLS])
+
+if test "x$NLS" = "xyes"; then
+  NLS="-DNLS"
+else
+  NLS=
+fi
+AC_SUBST(NLS)
+
+dnl Determine if maintainer portions of the Makefiles should be included.
+AM_MAINTAINER_MODE
+
+dnl Output files
+AM_CONFIG_HEADER(config.h)
+AC_CONFIG_FILES([
+version.h
+Makefile
+data/Makefile
+data/styles/Makefile
+doc/Makefile
+doc/fr_FR/Makefile
+doc/ja_JP/Makefile
+doc/nl_NL/Makefile
+doc/sl_SI/Makefile
+lib/Makefile
+lib/libbt.pc
+src/Makefile
+util/Makefile
+])
+AC_OUTPUT
+
+dnl Print results
+AC_MSG_RESULT([])
+AC_MSG_RESULT([	$PACKAGE version $VERSION configured successfully.])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([Using '$prefix' for installation.])
+AC_MSG_RESULT([Using '$CXX' for C++ compiler.])
+AC_MSG_RESULT([Building with '$CPPFLAGS $CXXFLAGS' for C++ compiler flags.])
+AC_MSG_RESULT([Building with '$LDFLAGS $LIBS' libraries.])
+AC_MSG_RESULT([])
diff --git a/.pc/090_focus.patch/src/blackbox.cc b/.pc/090_focus.patch/src/blackbox.cc
new file mode 100644
index 0000000..df816da
--- /dev/null
+++ b/.pc/090_focus.patch/src/blackbox.cc
@@ -0,0 +1,624 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// blackbox.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh at debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "blackbox.hh"
+#include "Screen.hh"
+#include "Slit.hh"
+#include "Window.hh"
+
+#include <Pen.hh>
+#include <PixmapCache.hh>
+#include <Util.hh>
+
+#include <X11/Xresource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+
+// #define FOCUS_DEBUG
+#ifdef FOCUS_DEBUG
+static const char *Mode[] = {
+  "Normal",
+  "Grab",
+  "Ungrab",
+  "WhileGrabbed"
+};
+
+static const char *Detail[] = {
+  "Ancestor",
+  "Virtual",
+  "Inferior",
+  "Nonlinear",
+  "NonlinearVirtual",
+  "Pointer",
+  "PointerRoot",
+  "DetailNone"
+};
+#endif // FOCUS_DEBUG
+
+
+void Blackbox::save_rc(void)
+{ _resource.save(*this); }
+
+
+void Blackbox::load_rc(void)
+{ _resource.load(*this); }
+
+
+void Blackbox::reload_rc(void) {
+  load_rc();
+  reconfigure();
+}
+
+
+void Blackbox::init_icccm(void) {
+  char* atoms[7] = {
+    "WM_COLORMAP_WINDOWS",
+    "WM_PROTOCOLS",
+    "WM_STATE",
+    "WM_CHANGE_STATE",
+    "WM_DELETE_WINDOW",
+    "WM_TAKE_FOCUS",
+    "_MOTIF_WM_HINTS"
+  };
+  Atom atoms_return[7];
+  XInternAtoms(XDisplay(), atoms, 7, false, atoms_return);
+  xa_wm_colormap_windows = atoms_return[0];
+  xa_wm_protocols = atoms_return[1];
+  xa_wm_state = atoms_return[2];
+  xa_wm_change_state = atoms_return[3];
+  xa_wm_delete_window = atoms_return[4];
+  xa_wm_take_focus = atoms_return[5];
+  motif_wm_hints = atoms_return[6];
+
+  _ewmh = new bt::EWMH(display());
+}
+
+
+void Blackbox::updateActiveWindow() const {
+  Window active = (focused_window) ? focused_window->clientWindow() : None;
+  for (unsigned int i = 0; i < display().screenCount(); ++i)
+    _ewmh->setActiveWindow(display().screenInfo(i).rootWindow(), active);
+}
+
+
+void Blackbox::shutdown(void) {
+  bt::Application::shutdown();
+
+  XGrabServer();
+
+  XSetInputFocus(XDisplay(), PointerRoot, RevertToPointerRoot, XTime());
+
+  std::for_each(screen_list, screen_list + screen_list_count,
+                std::mem_fun(&BScreen::shutdown));
+
+  XSync(XDisplay(), false);
+
+  XUngrabServer();
+}
+
+
+static Bool scanForFocusIn(Display *, XEvent *e, XPointer) {
+  if (e->type == FocusIn
+      && e->xfocus.mode != NotifyGrab
+      && (e->xfocus.detail == NotifyNonlinearVirtual
+          || e->xfocus.detail == NotifyVirtual)) {
+    return true;
+  }
+  return false;
+}
+
+
+void Blackbox::process_event(XEvent *e) {
+  switch (e->type) {
+  case MapRequest: {
+#ifdef    DEBUG
+    fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
+            e->xmaprequest.window);
+#endif // DEBUG
+
+    BlackboxWindow *win = findWindow(e->xmaprequest.window);
+
+    if (win) {
+      if ((!activeScreen() || activeScreen() == win->screen()) &&
+          (win->isTransient() || _resource.focusNewWindows()))
+        win->activate();
+    } else {
+      BScreen *screen = findScreen(e->xmaprequest.parent);
+
+      if (! screen) {
+        /*
+          we got a map request for a window who's parent isn't root. this
+          can happen in only one circumstance:
+
+          a client window unmapped a managed window, and then remapped it
+          somewhere between unmapping the client window and reparenting it
+          to root.
+
+          regardless of how it happens, we need to find the screen that
+          the window is on
+        */
+        XWindowAttributes wattrib;
+        if (! XGetWindowAttributes(XDisplay(), e->xmaprequest.window,
+                                   &wattrib)) {
+          // failed to get the window attributes, perhaps the window has
+          // now been destroyed?
+          break;
+        }
+
+        screen = findScreen(wattrib.root);
+        assert(screen != 0); // this should never happen
+      }
+      screen->addWindow(e->xmaprequest.window);
+    }
+
+    break;
+  }
+
+  case ConfigureRequest: {
+    BlackboxWindow *win = findWindow(e->xconfigurerequest.window);
+    if (win) {
+      // a window wants to resize
+      win->configureRequestEvent(&e->xconfigurerequest);
+      break;
+    }
+
+    Slit *slit =
+      dynamic_cast<Slit *>(findEventHandler(e->xconfigurerequest.parent));
+    if (slit) {
+      // something in the slit wants to resize
+      slit->configureRequestEvent(&e->xconfigurerequest);
+      break;
+    }
+
+    /*
+      handle configure requests for windows that have no EventHandlers
+      by simply configuring them as requested.
+
+      note: the event->window parameter points to the window being
+      configured, and event->parent points to the window that received
+      the event (in this case, the root window, since
+      SubstructureRedirect has been selected).
+    */
+    XWindowChanges xwc;
+    xwc.x = e->xconfigurerequest.x;
+    xwc.y = e->xconfigurerequest.y;
+    xwc.width = e->xconfigurerequest.width;
+    xwc.height = e->xconfigurerequest.height;
+    xwc.border_width = e->xconfigurerequest.border_width;
+    xwc.sibling = e->xconfigurerequest.above;
+    xwc.stack_mode = e->xconfigurerequest.detail;
+    XConfigureWindow(XDisplay(),
+                     e->xconfigurerequest.window,
+                     e->xconfigurerequest.value_mask,
+                     &xwc);
+    break;
+  }
+
+  case FocusIn: {
+#ifdef FOCUS_DEBUG
+    printf("FocusIn : window %8lx mode %s detail %s\n",
+           e->xfocus.window, Mode[e->xfocus.mode], Detail[e->xfocus.detail]);
+#endif
+
+    if (e->xfocus.mode == NotifyGrab
+        || (e->xfocus.detail != NotifyNonlinearVirtual
+            && e->xfocus.detail != NotifyVirtual)) {
+      /*
+        don't process FocusIns when:
+        1. they are the result of a grab
+        2. the new focus window isn't an ancestor or inferior of the
+        old focus window (NotifyNonlinearVirtual and NotifyVirtual)
+      */
+      break;
+    }
+
+    BlackboxWindow *win = findWindow(e->xfocus.window);
+    if (!win || win->isFocused())
+      break;
+
+#ifdef FOCUS_DEBUG
+    printf("          win %p got focus\n", win);
+#endif
+    win->setFocused(true);
+    setFocusedWindow(win);
+
+    /*
+      set the event window to None.  when the FocusOut event handler calls
+      this function recursively, it uses this as an indication that focus
+      has moved to a known window.
+    */
+    e->xfocus.window = None;
+
+    break;
+  }
+
+  case FocusOut: {
+#ifdef FOCUS_DEBUG
+    printf("FocusOut: window %8lx mode %s detail %s\n",
+           e->xfocus.window, Mode[e->xfocus.mode], Detail[e->xfocus.detail]);
+#endif
+
+    if (e->xfocus.mode == NotifyGrab
+        || (e->xfocus.detail != NotifyNonlinearVirtual
+            && e->xfocus.detail != NotifyVirtual)) {
+      /*
+        don't process FocusOuts when:
+        1. they are the result of a grab
+        2. the new focus window isn't an ancestor or inferior of the
+        old focus window (NotifyNonlinearVirtual and NotifyNonlinearVirtual)
+      */
+      break;
+    }
+
+    BlackboxWindow *win = findWindow(e->xfocus.window);
+    if (!win || !win->isFocused())
+      break;
+
+    bool lost_focus = true; // did the window really lose focus?
+    bool no_focus = true;   // did another window get focus?
+
+    XEvent event;
+    if (XCheckIfEvent(XDisplay(), &event, scanForFocusIn, NULL)) {
+      process_event(&event);
+
+      if (event.xfocus.window == None)
+        no_focus = false;
+    } else {
+      XWindowAttributes attr;
+      Window w;
+      int unused;
+      XGetInputFocus(XDisplay(), &w, &unused);
+      if (w != None
+          && XGetWindowAttributes(XDisplay(), w, &attr)
+          && attr.override_redirect) {
+#ifdef FOCUS_DEBUG
+        printf("          focused moved to an override_redirect window\n");
+#endif
+        lost_focus = (e->xfocus.mode == NotifyNormal);
+      }
+    }
+
+    if (lost_focus) {
+#ifdef FOCUS_DEBUG
+      printf("          win %p lost focus\n", win);
+#endif
+      win->setFocused(false);
+
+      if (no_focus) {
+#ifdef FOCUS_DEBUG
+        printf("          no window has focus\n");
+#endif
+        setFocusedWindow(0);
+      }
+    }
+
+    break;
+  }
+
+  default:
+    // Send the event through the default EventHandlers.
+    bt::Application::process_event(e);
+    break;
+  } // switch
+}
+
+
+bool Blackbox::process_signal(int sig) {
+  switch (sig) {
+  case SIGHUP:
+    reconfigure();
+    break;
+
+  case SIGUSR1:
+    reload_rc();
+    break;
+
+  case SIGUSR2:
+    rereadMenu();
+    break;
+
+  default:
+    return bt::Application::process_signal(sig);
+  } // switch
+
+  return true;
+}
+
+
+void Blackbox::timeout(bt::Timer *) {
+  XrmDatabase new_blackboxrc = (XrmDatabase) 0;
+
+  std::string style = "session.styleFile: ";
+  style += _resource.styleFilename();
+  XrmPutLineResource(&new_blackboxrc, style.c_str());
+
+  XrmDatabase old_blackboxrc = XrmGetFileDatabase(_resource.rcFilename());
+
+  XrmMergeDatabases(new_blackboxrc, &old_blackboxrc);
+  XrmPutFileDatabase(old_blackboxrc, _resource.rcFilename());
+  if (old_blackboxrc) XrmDestroyDatabase(old_blackboxrc);
+
+  std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
+                bt::PointerAssassin());
+  menuTimestamps.clear();
+
+  std::for_each(screen_list, screen_list + screen_list_count,
+                std::mem_fun(&BScreen::reconfigure));
+
+  bt::Font::clearCache();
+  bt::PixmapCache::clearCache();
+  bt::Pen::clearCache();
+
+  // clear the color cache here to allow the pen cache to deallocate
+  // all unused colors
+  bt::Color::clearCache();
+}
+
+
+Blackbox::Blackbox(char **m_argv, const char *dpy_name,
+                   const std::string& rc, bool multi_head)
+  : bt::Application(m_argv[0], dpy_name, multi_head),
+    grab_count(0u), _resource(rc)
+{
+  if (! XSupportsLocale())
+    fprintf(stderr, "X server does not support locale\n");
+
+  if (XSetLocaleModifiers("") == NULL)
+    fprintf(stderr, "cannot set locale modifiers\n");
+
+  argv = m_argv;
+
+  active_screen = 0;
+  focused_window = (BlackboxWindow *) 0;
+  _ewmh = (bt::EWMH*) 0;
+
+  init_icccm();
+
+  if (! multi_head || display().screenCount() == 1)
+    screen_list_count = 1;
+  else
+    screen_list_count = display().screenCount();
+
+  _resource.load(*this);
+
+  screen_list = new BScreen*[screen_list_count];
+  unsigned int managed = 0;
+  for (unsigned int i = 0; i < screen_list_count; ++i) {
+    BScreen *screen = new BScreen(this, i);
+
+    if (! screen->isScreenManaged()) {
+      delete screen;
+      continue;
+    }
+
+    screen_list[i] = screen;
+    ++managed;
+  }
+
+  if (managed == 0) {
+    fprintf(stderr, "%s: no manageable screens found, exiting...\n",
+            applicationName().c_str());
+    ::exit(3);
+  }
+
+  screen_list_count = managed;
+
+  // start with the first managed screen as the active screen
+  setActiveScreen(screen_list[0]);
+
+  XSynchronize(XDisplay(), false);
+  XSync(XDisplay(), false);
+
+  timer = new bt::Timer(this, this);
+  timer->setTimeout(0l);
+}
+
+
+Blackbox::~Blackbox(void) {
+  std::for_each(screen_list, screen_list + screen_list_count,
+                bt::PointerAssassin());
+
+  delete [] screen_list;
+  std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
+                bt::PointerAssassin());
+
+  delete timer;
+  delete _ewmh;
+}
+
+
+void Blackbox::XGrabServer(void) {
+  if (grab_count++ == 0)
+    ::XGrabServer(XDisplay());
+}
+
+
+void Blackbox::XUngrabServer(void) {
+  if (--grab_count == 0)
+    ::XUngrabServer(XDisplay());
+}
+
+
+BScreen *Blackbox::findScreen(Window window) const {
+  for (unsigned int i = 0; i < screen_list_count; ++i)
+    if (screen_list[i]->screenInfo().rootWindow() == window)
+      return screen_list[i];
+  return 0;
+}
+
+
+void Blackbox::setActiveScreen(BScreen *screen) {
+  if (active_screen && active_screen == screen) // nothing to do
+    return;
+
+  assert(screen != 0);
+  active_screen = screen;
+
+  // install screen colormap
+  XInstallColormap(XDisplay(), active_screen->screenInfo().colormap());
+
+  if (! focused_window || focused_window->screen() != active_screen)
+    setFocusedWindow(0);
+}
+
+
+BScreen* Blackbox::screenNumber(unsigned int n) {
+  assert(n < screen_list_count);
+  return screen_list[n];
+}
+
+
+BlackboxWindow *Blackbox::findWindow(Window window) const {
+  WindowLookup::const_iterator it = windowSearchList.find(window);
+  if (it != windowSearchList.end())
+    return it->second;
+  return 0;
+}
+
+
+void Blackbox::insertWindow(Window window, BlackboxWindow *data)
+{ windowSearchList.insert(WindowLookupPair(window, data)); }
+
+
+void Blackbox::removeWindow(Window window)
+{ windowSearchList.erase(window); }
+
+
+BWindowGroup *Blackbox::findWindowGroup(Window window) const {
+  GroupLookup::const_iterator it = groupSearchList.find(window);
+  if (it != groupSearchList.end())
+    return it->second;
+  return 0;
+}
+
+
+void Blackbox::insertWindowGroup(Window window, BWindowGroup *data)
+{ groupSearchList.insert(GroupLookupPair(window, data)); }
+
+
+void Blackbox::removeWindowGroup(Window window)
+{ groupSearchList.erase(window); }
+
+
+void Blackbox::setFocusedWindow(BlackboxWindow *win) {
+  if (focused_window && focused_window == win) // nothing to do
+    return;
+
+  if (win && !win->isIconic()) {
+    // the active screen is the one with the newly focused window...
+    active_screen = win->screen();
+    focused_window = win;
+  } else {
+    // nothing has focus
+    focused_window = 0;
+    assert(active_screen != 0);
+    XSetInputFocus(XDisplay(), active_screen->noFocusWindow(),
+                   RevertToPointerRoot, XTime());
+  }
+
+  updateActiveWindow();
+}
+
+
+void Blackbox::restart(const std::string &prog) {
+  setRunState(bt::Application::SHUTDOWN);
+
+  /*
+    since we don't allow control to return to the eventloop, we need
+    to call shutdown() explicitly
+  */
+  shutdown();
+
+  if (! prog.empty()) {
+    putenv(const_cast<char *>
+           (display().screenInfo(0).displayString().c_str()));
+    execlp(prog.c_str(), prog.c_str(), NULL);
+    perror(prog.c_str());
+  }
+
+  // fall back in case the above execlp doesn't work
+  execvp(argv[0], argv);
+  std::string name = bt::basename(argv[0]);
+  execvp(name.c_str(), argv);
+}
+
+
+void Blackbox::reconfigure(void) {
+  if (! timer->isTiming())
+    timer->start();
+}
+
+
+void Blackbox::saveMenuFilename(const std::string& filename) {
+  assert(!filename.empty());
+  bool found = false;
+
+  MenuTimestampList::iterator it = menuTimestamps.begin();
+  for (; it != menuTimestamps.end() && !found; ++it)
+    found = (*it)->filename == filename;
+  if (found)
+    return;
+
+  struct stat buf;
+  if (stat(filename.c_str(), &buf) != 0)
+    return; // file doesn't exist
+
+  MenuTimestamp *ts = new MenuTimestamp;
+  ts->filename = filename;
+  ts->timestamp = buf.st_ctime;
+  menuTimestamps.push_back(ts);
+}
+
+
+void Blackbox::checkMenu(void) {
+  bool reread = false;
+  MenuTimestampList::iterator it = menuTimestamps.begin();
+  for(; it != menuTimestamps.end(); ++it) {
+    MenuTimestamp *tmp = *it;
+    struct stat buf;
+
+    if (! stat(tmp->filename.c_str(), &buf)) {
+      if (tmp->timestamp != buf.st_ctime)
+        reread = true;
+    } else {
+      reread = true;
+    }
+  }
+
+  if (reread)
+    rereadMenu();
+}
+
+
+void Blackbox::rereadMenu(void) {
+  std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
+                bt::PointerAssassin());
+  menuTimestamps.clear();
+
+  std::for_each(screen_list, screen_list + screen_list_count,
+                std::mem_fun(&BScreen::rereadMenu));
+}
diff --git a/.pc/100_Prevent-crashes-on-apps-disabled-minimize-or-maximiz.patch/src/Window.cc b/.pc/100_Prevent-crashes-on-apps-disabled-minimize-or-maximiz.patch/src/Window.cc
new file mode 100644
index 0000000..b1361ae
--- /dev/null
+++ b/.pc/100_Prevent-crashes-on-apps-disabled-minimize-or-maximiz.patch/src/Window.cc
@@ -0,0 +1,4108 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Window.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+// make sure we get bt::textPropertyToString()
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "Window.hh"
+#include "Screen.hh"
+#include "WindowGroup.hh"
+#include "Windowmenu.hh"
+#include "Workspace.hh"
+#include "blackbox.hh"
+
+#include <Pen.hh>
+#include <PixmapCache.hh>
+#include <Unicode.hh>
+
+#include <X11/Xatom.h>
+#ifdef SHAPE
+#  include <X11/extensions/shape.h>
+#endif
+
+#include <assert.h>
+
+/*
+  sometimes C++ is a pain in the ass... it gives us stuff like the
+  default copy constructor and assignment operator (which does member
+  my member copy/assignment), but what we don't get is a default
+  comparison operator... how fucked up is that?
+
+  the language is designed to handle this:
+
+  struct foo { int a; int b };
+  foo a = { 0 , 0 }, b = a;
+  foo c;
+  c = a;
+
+  BUT, BUT, BUT, forget about doing this:
+
+  a = findFoo();
+  if (c == a)
+     return; // nothing to do
+  c = a;
+
+  ARGH!@#)(!*@#)(@#*$(!@#
+*/
+bool operator==(const WMNormalHints &x, const WMNormalHints &y);
+
+
+// Event mask used for managed client windows.
+const unsigned long client_window_event_mask =
+  (PropertyChangeMask | StructureNotifyMask);
+
+
+/*
+ * Returns the appropriate WindowType based on the _NET_WM_WINDOW_TYPE
+ */
+static WindowType window_type_from_atom(const bt::EWMH &ewmh, Atom atom) {
+  if (atom == ewmh.wmWindowTypeDialog())
+    return WindowTypeDialog;
+  if (atom == ewmh.wmWindowTypeDesktop())
+    return WindowTypeDesktop;
+  if (atom == ewmh.wmWindowTypeDock())
+    return WindowTypeDock;
+  if (atom == ewmh.wmWindowTypeMenu())
+    return WindowTypeMenu;
+  if (atom == ewmh.wmWindowTypeSplash())
+    return WindowTypeSplash;
+  if (atom == ewmh.wmWindowTypeToolbar())
+    return WindowTypeToolbar;
+  if (atom == ewmh.wmWindowTypeUtility())
+    return WindowTypeUtility;
+  return WindowTypeNormal;
+}
+
+
+/*
+ * Determine the appropriate decorations and functions based on the
+ * given properties and hints.
+ */
+static void update_decorations(WindowDecorationFlags &decorations,
+                               WindowFunctionFlags &functions,
+                               bool transient,
+                               const EWMH &ewmh,
+                               const MotifHints &motifhints,
+                               const WMNormalHints &wmnormal,
+                               const WMProtocols &wmprotocols) {
+  decorations = AllWindowDecorations;
+  functions   = AllWindowFunctions;
+
+  // transients should be kept on the same workspace are their parents
+  if (transient)
+    functions &= ~WindowFunctionChangeWorkspace;
+
+  // modify the window decorations/functions based on window type
+  switch (ewmh.window_type) {
+  case WindowTypeDialog:
+    decorations &= ~(WindowDecorationIconify |
+                     WindowDecorationMaximize);
+    functions   &= ~(WindowFunctionShade |
+                     WindowFunctionIconify |
+                     WindowFunctionMaximize |
+                     WindowFunctionChangeLayer |
+                     WindowFunctionFullScreen);
+    break;
+
+  case WindowTypeDesktop:
+  case WindowTypeDock:
+  case WindowTypeSplash:
+    decorations = NoWindowDecorations;
+    functions   = NoWindowFunctions;
+    break;
+
+  case WindowTypeToolbar:
+  case WindowTypeMenu:
+    decorations &= ~(WindowDecorationHandle |
+                     WindowDecorationGrip |
+                     WindowDecorationBorder |
+                     WindowDecorationIconify |
+                     WindowDecorationMaximize);
+    functions   &= ~(WindowFunctionResize |
+                     WindowFunctionShade |
+                     WindowFunctionIconify |
+                     WindowFunctionMaximize |
+                     WindowFunctionFullScreen);
+    break;
+
+  case WindowTypeUtility:
+    decorations &= ~(WindowDecorationIconify |
+                     WindowDecorationMaximize);
+    functions   &= ~(WindowFunctionShade |
+                     WindowFunctionIconify |
+                     WindowFunctionMaximize);
+    break;
+
+  default:
+    break;
+  }
+
+  // mask away stuff turned off by Motif hints
+  decorations &= motifhints.decorations;
+  functions   &= motifhints.functions;
+
+  // disable shade if we do not have a titlebar
+  if (!(decorations & WindowDecorationTitlebar))
+    functions &= ~WindowFunctionShade;
+
+  // disable grips and maximize if we have a fixed size window
+  if ((wmnormal.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize)
+      && wmnormal.max_width <= wmnormal.min_width
+      && wmnormal.max_height <= wmnormal.min_height) {
+    decorations &= ~(WindowDecorationMaximize |
+                     WindowDecorationGrip);
+    functions   &= ~(WindowFunctionResize |
+                     WindowFunctionMaximize);
+  }
+
+  // cannot close if client doesn't understand WM_DELETE_WINDOW
+  if (!wmprotocols.wm_delete_window) {
+    decorations &= ~WindowDecorationClose;
+    functions   &= ~WindowFunctionClose;
+  }
+}
+
+
+/*
+ * Calculate the frame margin based on the given decorations and
+ * style.
+ */
+static
+bt::EWMH::Strut update_margin(WindowDecorationFlags decorations,
+                              const WindowStyle &style) {
+  bt::EWMH::Strut margin;
+
+  const unsigned int bw = ((decorations & WindowDecorationBorder)
+                           ? style.frame_border_width
+                           : 0u);
+  margin.top = margin.bottom = margin.left = margin.right = bw;
+
+  if (decorations & WindowDecorationTitlebar)
+    margin.top += style.title_height - bw;
+
+  if (decorations & WindowDecorationHandle)
+    margin.bottom += style.handle_height - bw;
+
+  return margin;
+}
+
+
+/*
+ * Add specified window to the appropriate window group, creating a
+ * new group if necessary.
+ */
+static BWindowGroup *update_window_group(Window window_group,
+                                         Blackbox *blackbox,
+                                         BlackboxWindow *win) {
+  BWindowGroup *group = win->findWindowGroup();
+  if (!group) {
+    new BWindowGroup(blackbox, window_group);
+    group = win->findWindowGroup();
+    assert(group != 0);
+  }
+  group->addWindow(win);
+  return group;
+}
+
+
+/*
+ * Calculate the size of the frame window and constrain it to the
+ * size specified by the size hints of the client window.
+ *
+ * 'rect' refers to the geometry of the frame in pixels.
+ */
+enum Corner {
+  TopLeft,
+  TopRight,
+  BottomLeft,
+  BottomRight
+};
+static bt::Rect constrain(const bt::Rect &rect,
+                          const bt::EWMH::Strut &margin,
+                          const WMNormalHints &wmnormal,
+                          Corner corner) {
+  bt::Rect r;
+
+  // 'rect' represents the requested frame size, we need to strip
+  // 'margin' off and constrain the client size
+  r.setCoords(rect.left() + static_cast<signed>(margin.left),
+              rect.top() + static_cast<signed>(margin.top),
+              rect.right() - static_cast<signed>(margin.right),
+              rect.bottom() - static_cast<signed>(margin.bottom));
+
+  unsigned int dw = r.width(), dh = r.height();
+
+  const unsigned int base_width = (wmnormal.base_width
+                                   ? wmnormal.base_width
+                                   : wmnormal.min_width),
+                    base_height = (wmnormal.base_height
+                                   ? wmnormal.base_height
+                                   : wmnormal.min_height);
+
+  // fit to min/max size
+  if (dw < wmnormal.min_width)
+    dw = wmnormal.min_width;
+  if (dh < wmnormal.min_height)
+    dh = wmnormal.min_height;
+
+  if (dw > wmnormal.max_width)
+    dw = wmnormal.max_width;
+  if (dh > wmnormal.max_height)
+    dh = wmnormal.max_height;
+
+  assert(dw >= base_width && dh >= base_height);
+
+  // fit to size increments
+  if (wmnormal.flags & PResizeInc) {
+    dw = (((dw - base_width) / wmnormal.width_inc)
+          * wmnormal.width_inc) + base_width;
+    dh = (((dh - base_height) / wmnormal.height_inc)
+          * wmnormal.height_inc) + base_height;
+  }
+
+  /*
+   * honor aspect ratios (based on twm which is based on uwm)
+   *
+   * The math looks like this:
+   *
+   * minAspectX    dwidth     maxAspectX
+   * ---------- <= ------- <= ----------
+   * minAspectY    dheight    maxAspectY
+   *
+   * If that is multiplied out, then the width and height are
+   * invalid in the following situations:
+   *
+   * minAspectX * dheight > minAspectY * dwidth
+   * maxAspectX * dheight < maxAspectY * dwidth
+   *
+   */
+  if (wmnormal.flags & PAspect) {
+    unsigned int delta;
+    const unsigned int min_asp_x = wmnormal.min_aspect_x,
+                       min_asp_y = wmnormal.min_aspect_y,
+                       max_asp_x = wmnormal.max_aspect_x,
+                       max_asp_y = wmnormal.max_aspect_y,
+                           w_inc = wmnormal.width_inc,
+                           h_inc = wmnormal.height_inc;
+    if (min_asp_x * dh > min_asp_y * dw) {
+      delta = ((min_asp_x * dh / min_asp_y - dw) * w_inc) / w_inc;
+      if (dw + delta <= wmnormal.max_width) {
+        dw += delta;
+      } else {
+        delta = ((dh - (dw * min_asp_y) / min_asp_x) * h_inc) / h_inc;
+        if (dh - delta >= wmnormal.min_height) dh -= delta;
+      }
+    }
+    if (max_asp_x * dh < max_asp_y * dw) {
+      delta = ((max_asp_y * dw / max_asp_x - dh) * h_inc) / h_inc;
+      if (dh + delta <= wmnormal.max_height) {
+        dh += delta;
+      } else {
+        delta = ((dw - (dh * max_asp_x) / max_asp_y) * w_inc) / w_inc;
+        if (dw - delta >= wmnormal.min_width) dw -= delta;
+      }
+    }
+  }
+
+  r.setSize(dw, dh);
+
+  // add 'margin' back onto 'r'
+  r.setCoords(r.left() - margin.left, r.top() - margin.top,
+              r.right() + margin.right, r.bottom() + margin.bottom);
+
+  // move 'r' to the specified corner
+  int dx = rect.right() - r.right();
+  int dy = rect.bottom() - r.bottom();
+
+  switch (corner) {
+  case TopLeft:
+    // nothing to do
+    break;
+
+  case TopRight:
+    r.setPos(r.x() + dx, r.y());
+    break;
+
+  case BottomLeft:
+    r.setPos(r.x(), r.y() + dy);
+    break;
+
+  case BottomRight:
+    r.setPos(r.x() + dx, r.y() + dy);
+    break;
+  }
+
+  return r;
+}
+
+
+/*
+ * Positions 'rect' according to the client window geometry and window
+ * gravity.
+ */
+static bt::Rect applyGravity(const bt::Rect &rect,
+                             const bt::EWMH::Strut &margin,
+                             int gravity) {
+  bt::Rect r;
+
+  // apply horizontal window gravity
+  switch (gravity) {
+  default:
+  case NorthWestGravity:
+  case SouthWestGravity:
+  case WestGravity:
+    r.setX(rect.x());
+    break;
+
+  case NorthGravity:
+  case SouthGravity:
+  case CenterGravity:
+    r.setX(rect.x() - (margin.left + margin.right) / 2);
+    break;
+
+  case NorthEastGravity:
+  case SouthEastGravity:
+  case EastGravity:
+    r.setX(rect.x() - (margin.left + margin.right) + 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setX(rect.x() - margin.left);
+    break;
+  }
+
+  // apply vertical window gravity
+  switch (gravity) {
+  default:
+  case NorthWestGravity:
+  case NorthEastGravity:
+  case NorthGravity:
+    r.setY(rect.y());
+    break;
+
+  case CenterGravity:
+  case EastGravity:
+  case WestGravity:
+    r.setY(rect.y() - ((margin.top + margin.bottom) / 2));
+    break;
+
+  case SouthWestGravity:
+  case SouthEastGravity:
+  case SouthGravity:
+    r.setY(rect.y() - (margin.bottom + margin.top) + 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setY(rect.y() - margin.top);
+    break;
+  }
+
+  r.setSize(rect.width() + margin.left + margin.right,
+            rect.height() + margin.top + margin.bottom);
+  return r;
+}
+
+
+/*
+ * The reverse of the applyGravity function.
+ *
+ * Positions 'rect' according to the frame window geometry and window
+ * gravity.
+ */
+static bt::Rect restoreGravity(const bt::Rect &rect,
+                               const bt::EWMH::Strut &margin,
+                               int gravity) {
+  bt::Rect r;
+
+  // restore horizontal window gravity
+  switch (gravity) {
+  default:
+  case NorthWestGravity:
+  case SouthWestGravity:
+  case WestGravity:
+    r.setX(rect.x());
+    break;
+
+  case NorthGravity:
+  case SouthGravity:
+  case CenterGravity:
+    r.setX(rect.x() + (margin.left + margin.right) / 2);
+    break;
+
+  case NorthEastGravity:
+  case SouthEastGravity:
+  case EastGravity:
+    r.setX(rect.x() + (margin.left + margin.right) - 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setX(rect.x() + margin.left);
+    break;
+  }
+
+  // restore vertical window gravity
+  switch (gravity) {
+  default:
+  case NorthWestGravity:
+  case NorthEastGravity:
+  case NorthGravity:
+    r.setY(rect.y());
+    break;
+
+  case CenterGravity:
+  case EastGravity:
+  case WestGravity:
+    r.setY(rect.y() + (margin.top + margin.bottom) / 2);
+    break;
+
+  case SouthWestGravity:
+  case SouthEastGravity:
+  case SouthGravity:
+    r.setY(rect.y() + (margin.top + margin.bottom) - 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setY(rect.y() + margin.top);
+    break;
+  }
+
+  r.setSize(rect.width() - margin.left - margin.right,
+            rect.height() - margin.top - margin.bottom);
+  return r;
+}
+
+
+static bt::ustring readWMName(Blackbox *blackbox, Window window) {
+  bt::ustring name;
+
+  if (!blackbox->ewmh().readWMName(window, name) || name.empty()) {
+    XTextProperty text_prop;
+
+    if (XGetWMName(blackbox->XDisplay(), window, &text_prop)) {
+      name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
+                                                    text_prop));
+      XFree((char *) text_prop.value);
+    }
+  }
+
+  if (name.empty())
+    name = bt::toUnicode("Unnamed");
+
+  return name;
+}
+
+
+static bt::ustring readWMIconName(Blackbox *blackbox, Window window) {
+  bt::ustring name;
+
+  if (!blackbox->ewmh().readWMIconName(window, name) || name.empty()) {
+    XTextProperty text_prop;
+    if (XGetWMIconName(blackbox->XDisplay(), window, &text_prop)) {
+      name = bt::toUnicode(bt::textPropertyToString(blackbox->XDisplay(),
+                                                    text_prop));
+      XFree((char *) text_prop.value);
+    }
+  }
+
+  if (name.empty())
+    return bt::ustring();
+
+  return name;
+}
+
+
+static EWMH readEWMH(const bt::EWMH &bewmh,
+                     Window window,
+                     int currentWorkspace) {
+  EWMH ewmh;
+  ewmh.window_type  = WindowTypeNormal;
+  ewmh.workspace    = 0; // initialized properly below
+  ewmh.modal        = false;
+  ewmh.maxv         = false;
+  ewmh.maxh         = false;
+  ewmh.shaded       = false;
+  ewmh.skip_taskbar = false;
+  ewmh.skip_pager   = false;
+  ewmh.hidden       = false;
+  ewmh.fullscreen   = false;
+  ewmh.above        = false;
+  ewmh.below        = false;
+
+  // note: wm_name and wm_icon_name are read separately
+
+  bool ret;
+
+  bt::EWMH::AtomList atoms;
+  ret = bewmh.readWMWindowType(window, atoms);
+  if (ret) {
+    bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
+    for (; it != end; ++it) {
+      if (bewmh.isSupportedWMWindowType(*it)) {
+        ewmh.window_type = ::window_type_from_atom(bewmh, *it);
+        break;
+      }
+    }
+  }
+
+  atoms.clear();
+  ret = bewmh.readWMState(window, atoms);
+  if (ret) {
+    bt::EWMH::AtomList::iterator it = atoms.begin(), end = atoms.end();
+    for (; it != end; ++it) {
+      Atom state = *it;
+      if (state == bewmh.wmStateModal()) {
+        ewmh.modal = true;
+      } else if (state == bewmh.wmStateMaximizedVert()) {
+        ewmh.maxv = true;
+      } else if (state == bewmh.wmStateMaximizedHorz()) {
+        ewmh.maxh = true;
+      } else if (state == bewmh.wmStateShaded()) {
+        ewmh.shaded = true;
+      } else if (state == bewmh.wmStateSkipTaskbar()) {
+        ewmh.skip_taskbar = true;
+      } else if (state == bewmh.wmStateSkipPager()) {
+        ewmh.skip_pager = true;
+      } else if (state == bewmh.wmStateHidden()) {
+        /*
+          ignore _NET_WM_STATE_HIDDEN if present, the wm sets this
+          state, not the application
+        */
+      } else if (state == bewmh.wmStateFullscreen()) {
+        ewmh.fullscreen = true;
+      } else if (state == bewmh.wmStateAbove()) {
+        ewmh.above = true;
+      } else if (state == bewmh.wmStateBelow()) {
+        ewmh.below = true;
+      }
+    }
+  }
+
+  switch (ewmh.window_type) {
+  case WindowTypeDesktop:
+  case WindowTypeDock:
+    // these types should occupy all workspaces by default
+    ewmh.workspace = bt::BSENTINEL;
+    break;
+
+  default:
+    if (!bewmh.readWMDesktop(window, ewmh.workspace))
+      ewmh.workspace = currentWorkspace;
+    break;
+  } //switch
+
+  return ewmh;
+}
+
+
+/*
+ * Returns the MotifWM hints for the specified window.
+ */
+static MotifHints readMotifWMHints(Blackbox *blackbox, Window window) {
+  MotifHints motif;
+  motif.decorations = AllWindowDecorations;
+  motif.functions   = AllWindowFunctions;
+
+  /*
+    this structure only contains 3 elements, even though the Motif 2.0
+    structure contains 5, because we only use the first 3
+  */
+  struct PropMotifhints {
+    unsigned long flags;
+    unsigned long functions;
+    unsigned long decorations;
+  };
+  static const unsigned int PROP_MWM_HINTS_ELEMENTS = 3u;
+  enum { // MWM flags
+    MWM_HINTS_FUNCTIONS   = 1<<0,
+    MWM_HINTS_DECORATIONS = 1<<1
+  };
+  enum { // MWM functions
+    MWM_FUNC_ALL      = 1<<0,
+    MWM_FUNC_RESIZE   = 1<<1,
+    MWM_FUNC_MOVE     = 1<<2,
+    MWM_FUNC_MINIMIZE = 1<<3,
+    MWM_FUNC_MAXIMIZE = 1<<4,
+    MWM_FUNC_CLOSE    = 1<<5
+  };
+  enum { // MWM decorations
+    MWM_DECOR_ALL      = 1<<0,
+    MWM_DECOR_BORDER   = 1<<1,
+    MWM_DECOR_RESIZEH  = 1<<2,
+    MWM_DECOR_TITLE    = 1<<3,
+    MWM_DECOR_MENU     = 1<<4,
+    MWM_DECOR_MINIMIZE = 1<<5,
+    MWM_DECOR_MAXIMIZE = 1<<6
+  };
+
+  Atom atom_return;
+  PropMotifhints *prop = 0;
+  int format;
+  unsigned long num, len;
+  int ret = XGetWindowProperty(blackbox->XDisplay(), window,
+                               blackbox->motifWmHintsAtom(), 0,
+                               PROP_MWM_HINTS_ELEMENTS, False,
+                               blackbox->motifWmHintsAtom(), &atom_return,
+                               &format, &num, &len,
+                               (unsigned char **) &prop);
+
+  if (ret != Success || !prop || num != PROP_MWM_HINTS_ELEMENTS) {
+    if (prop) XFree(prop);
+    return motif;
+  }
+
+  if (prop->flags & MWM_HINTS_FUNCTIONS) {
+    if (prop->functions & MWM_FUNC_ALL) {
+      motif.functions = AllWindowFunctions;
+    } else {
+      // default to the functions that cannot be set through
+      // _MOTIF_WM_HINTS
+      motif.functions = (WindowFunctionShade
+                         | WindowFunctionChangeWorkspace
+                         | WindowFunctionChangeLayer
+                         | WindowFunctionFullScreen);
+
+      if (prop->functions & MWM_FUNC_RESIZE)
+        motif.functions |= WindowFunctionResize;
+      if (prop->functions & MWM_FUNC_MOVE)
+        motif.functions |= WindowFunctionMove;
+      if (prop->functions & MWM_FUNC_MINIMIZE)
+        motif.functions |= WindowFunctionIconify;
+      if (prop->functions & MWM_FUNC_MAXIMIZE)
+        motif.functions |= WindowFunctionMaximize;
+      if (prop->functions & MWM_FUNC_CLOSE)
+        motif.functions |= WindowFunctionClose;
+    }
+  }
+
+  if (prop->flags & MWM_HINTS_DECORATIONS) {
+    if (prop->decorations & MWM_DECOR_ALL) {
+      motif.decorations = AllWindowDecorations;
+    } else {
+      motif.decorations = NoWindowDecorations;
+
+      if (prop->decorations & MWM_DECOR_BORDER)
+        motif.decorations |= WindowDecorationBorder;
+      if (prop->decorations & MWM_DECOR_RESIZEH)
+        motif.decorations |= WindowDecorationHandle;
+      if (prop->decorations & MWM_DECOR_TITLE)
+        motif.decorations |= WindowDecorationTitlebar;
+      if (prop->decorations & MWM_DECOR_MINIMIZE)
+        motif.decorations |= WindowDecorationIconify;
+      if (prop->decorations & MWM_DECOR_MAXIMIZE)
+        motif.decorations |= WindowDecorationMaximize;
+    }
+  }
+
+  if (motif.decorations & WindowDecorationHandle) {
+    if (motif.functions & WindowFunctionResize)
+      motif.decorations |= WindowDecorationGrip;
+    else
+      motif.decorations &= ~WindowDecorationGrip;
+  }
+
+  if (motif.decorations & WindowDecorationTitlebar) {
+    if (motif.functions & WindowFunctionClose)
+      motif.decorations |= WindowDecorationClose;
+    else
+      motif.decorations &= ~WindowDecorationClose;
+  }
+
+  XFree(prop);
+
+  return motif;
+}
+
+
+/*
+ * Returns the value of the WM_HINTS property.  If the property is not
+ * set, a set of default values is returned instead.
+ */
+static WMHints readWMHints(Blackbox *blackbox, Window window) {
+  WMHints wmh;
+  wmh.accept_focus = false;
+  wmh.window_group = None;
+  wmh.initial_state = NormalState;
+
+  XWMHints *wmhint = XGetWMHints(blackbox->XDisplay(), window);
+  if (!wmhint) return wmh;
+
+  if (wmhint->flags & InputHint)
+    wmh.accept_focus = (wmhint->input == True);
+  if (wmhint->flags & StateHint)
+    wmh.initial_state = wmhint->initial_state;
+  if (wmhint->flags & WindowGroupHint)
+    wmh.window_group = wmhint->window_group;
+
+  XFree(wmhint);
+
+  return wmh;
+}
+
+
+/*
+ * Returns the value of the WM_NORMAL_HINTS property.  If the property
+ * is not set, a set of default values is returned instead.
+ */
+static WMNormalHints readWMNormalHints(Blackbox *blackbox,
+                                       Window window,
+                                       const bt::ScreenInfo &screenInfo) {
+  WMNormalHints wmnormal;
+  wmnormal.flags = 0;
+  wmnormal.min_width    = wmnormal.min_height   = 1u;
+  wmnormal.width_inc    = wmnormal.height_inc   = 1u;
+  wmnormal.min_aspect_x = wmnormal.min_aspect_y = 1u;
+  wmnormal.max_aspect_x = wmnormal.max_aspect_y = 1u;
+  wmnormal.base_width   = wmnormal.base_height  = 0u;
+  wmnormal.win_gravity  = NorthWestGravity;
+
+  /*
+    use the full screen, not the strut modified size. otherwise when
+    the availableArea changes max_width/height will be incorrect and
+    lead to odd rendering bugs.
+  */
+  const bt::Rect &rect = screenInfo.rect();
+  wmnormal.max_width = rect.width();
+  wmnormal.max_height = rect.height();
+
+  XSizeHints sizehint;
+  long unused;
+  if (! XGetWMNormalHints(blackbox->XDisplay(), window, &sizehint, &unused))
+    return wmnormal;
+
+  wmnormal.flags = sizehint.flags;
+
+  if (sizehint.flags & PMinSize) {
+    if (sizehint.min_width > 0)
+      wmnormal.min_width  = sizehint.min_width;
+    if (sizehint.min_height > 0)
+      wmnormal.min_height = sizehint.min_height;
+
+    /*
+      if the minimum size is bigger then the screen, adjust the
+      maximum size
+    */
+    if (wmnormal.min_width > wmnormal.max_width)
+      wmnormal.max_width = wmnormal.min_width;
+    if (wmnormal.min_height > wmnormal.max_height)
+      wmnormal.max_height = wmnormal.min_height;
+  }
+
+  if (sizehint.flags & PMaxSize) {
+    if (sizehint.max_width >= static_cast<signed>(wmnormal.min_width))
+      wmnormal.max_width  = sizehint.max_width;
+    else
+      wmnormal.max_width  = wmnormal.min_width;
+
+    if (sizehint.max_height >= static_cast<signed>(wmnormal.min_height))
+      wmnormal.max_height = sizehint.max_height;
+    else
+      wmnormal.max_height = wmnormal.min_height;
+  }
+
+  if (sizehint.flags & PResizeInc) {
+    wmnormal.width_inc  = sizehint.width_inc;
+    wmnormal.height_inc = sizehint.height_inc;
+  }
+
+  if (sizehint.flags & PAspect) {
+    wmnormal.min_aspect_x = sizehint.min_aspect.x;
+    wmnormal.min_aspect_y = sizehint.min_aspect.y;
+    wmnormal.max_aspect_x = sizehint.max_aspect.x;
+    wmnormal.max_aspect_y = sizehint.max_aspect.y;
+  }
+
+  if (sizehint.flags & PBaseSize) {
+    if (sizehint.base_width <= static_cast<signed>(wmnormal.min_width))
+      wmnormal.base_width  = sizehint.base_width;
+    if (sizehint.base_height <= static_cast<signed>(wmnormal.min_height))
+      wmnormal.base_height = sizehint.base_height;
+  }
+
+  if (sizehint.flags & PWinGravity)
+    wmnormal.win_gravity = sizehint.win_gravity;
+
+  return wmnormal;
+}
+
+
+/*
+ * Retrieve which Window Manager Protocols are supported by the client
+ * window.
+ */
+static WMProtocols readWMProtocols(Blackbox *blackbox,
+                                   Window window) {
+  WMProtocols protocols;
+  protocols.wm_delete_window = false;
+  protocols.wm_take_focus    = false;
+
+  Atom *proto;
+  int num_return = 0;
+
+  if (XGetWMProtocols(blackbox->XDisplay(), window,
+                      &proto, &num_return)) {
+    for (int i = 0; i < num_return; ++i) {
+      if (proto[i] == blackbox->wmDeleteWindowAtom()) {
+        protocols.wm_delete_window = true;
+      } else if (proto[i] == blackbox->wmTakeFocusAtom()) {
+        protocols.wm_take_focus = true;
+      }
+    }
+    XFree(proto);
+  }
+
+  return protocols;
+}
+
+
+/*
+ * Reads the value of the WM_TRANSIENT_FOR property and returns a
+ * pointer to the transient parent for this window.  If the
+ * WM_TRANSIENT_FOR is missing or invalid, this function returns 0.
+ *
+ * 'client.wmhints' should be properly updated before calling this
+ * function.
+ *
+ * Note: a return value of ~0ul signifies a window that should be
+ * transient but has no discernible parent.
+ */
+static Window readTransientInfo(Blackbox *blackbox,
+                                Window window,
+                                const bt::ScreenInfo &screenInfo,
+                                const WMHints &wmhints) {
+  Window trans_for = None;
+
+  if (!XGetTransientForHint(blackbox->XDisplay(), window, &trans_for)) {
+    // WM_TRANSIENT_FOR hint not set
+    return 0;
+  }
+
+  if (trans_for == window) {
+    // wierd client... treat this window as a normal window
+    return 0;
+  }
+
+  if (trans_for == None || trans_for == screenInfo.rootWindow()) {
+    /*
+      this is a violation of the ICCCM, yet the EWMH allows this as a
+      way to signify a group transient.
+    */
+    trans_for = wmhints.window_group;
+  }
+
+  return trans_for;
+}
+
+
+static bool readState(unsigned long &current_state,
+                      Blackbox *blackbox,
+                      Window window) {
+  current_state = NormalState;
+
+  Atom atom_return;
+  bool ret = false;
+  int foo;
+  unsigned long *state, ulfoo, nitems;
+
+  if ((XGetWindowProperty(blackbox->XDisplay(), window,
+                          blackbox->wmStateAtom(),
+                          0l, 2l, False, blackbox->wmStateAtom(),
+                          &atom_return, &foo, &nitems, &ulfoo,
+                          (unsigned char **) &state) != Success) ||
+      (! state)) {
+    return false;
+  }
+
+  if (nitems >= 1) {
+    current_state = static_cast<unsigned long>(state[0]);
+    ret = true;
+  }
+
+  XFree((void *) state);
+
+  return ret;
+}
+
+
+static void clearState(Blackbox *blackbox, Window window) {
+  XDeleteProperty(blackbox->XDisplay(), window, blackbox->wmStateAtom());
+
+  const bt::EWMH& ewmh = blackbox->ewmh();
+  ewmh.removeProperty(window, ewmh.wmDesktop());
+  ewmh.removeProperty(window, ewmh.wmState());
+  ewmh.removeProperty(window, ewmh.wmAllowedActions());
+  ewmh.removeProperty(window, ewmh.wmVisibleName());
+  ewmh.removeProperty(window, ewmh.wmVisibleIconName());
+}
+
+
+/*
+ * Initializes the class with default values/the window's set initial values.
+ */
+BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
+  // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
+  //         sizeof(BlackboxWindow));
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
+#endif // DEBUG
+
+  /*
+    set timer to zero... it is initialized properly later, so we check
+    if timer is zero in the destructor, and assume that the window is not
+    fully constructed if timer is zero...
+  */
+  timer = (bt::Timer*) 0;
+  blackbox = b;
+  client.window = w;
+  _screen = s;
+  lastButtonPressTime = 0;
+
+  /*
+    the server needs to be grabbed here to prevent client's from sending
+    events while we are in the process of managing their window.
+    We hold the grab until after we are done moving the window around.
+  */
+
+  blackbox->XGrabServer();
+
+  // fetch client size and placement
+  XWindowAttributes wattrib;
+  if (! XGetWindowAttributes(blackbox->XDisplay(),
+                             client.window, &wattrib) ||
+      ! wattrib.screen || wattrib.override_redirect) {
+#ifdef    DEBUG
+    fprintf(stderr,
+            "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
+#endif // DEBUG
+
+    blackbox->XUngrabServer();
+    delete this;
+    return;
+  }
+
+  // set the eventmask early in the game so that we make sure we get
+  // all the events we are interested in
+  XSetWindowAttributes attrib_set;
+  attrib_set.event_mask = ::client_window_event_mask;
+  attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
+                                     ButtonMotionMask;
+  XChangeWindowAttributes(blackbox->XDisplay(), client.window,
+                          CWEventMask|CWDontPropagate, &attrib_set);
+
+  client.colormap = wattrib.colormap;
+  window_number = bt::BSENTINEL;
+  client.strut = 0;
+  /*
+    set the initial size and location of client window (relative to the
+    _root window_). This position is the reference point used with the
+    window's gravity to find the window's initial position.
+  */
+  client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+  client.premax = client.rect;
+  client.old_bw = wattrib.border_width;
+  client.current_state = NormalState;
+
+  frame.window = frame.plate = frame.title = frame.handle = None;
+  frame.close_button = frame.iconify_button = frame.maximize_button = None;
+  frame.right_grip = frame.left_grip = None;
+  frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
+  frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
+  frame.pbutton = frame.ugrip = frame.fgrip = None;
+
+  timer = new bt::Timer(blackbox, this);
+  timer->setTimeout(blackbox->resource().autoRaiseDelay());
+
+  client.title = ::readWMName(blackbox, client.window);
+  client.icon_title = ::readWMIconName(blackbox, client.window);
+
+  // get size, aspect, minimum/maximum size, ewmh and other hints set
+  // by the client
+  client.ewmh = ::readEWMH(blackbox->ewmh(), client.window,
+                           _screen->currentWorkspace());
+  client.motif = ::readMotifWMHints(blackbox, client.window);
+  client.wmhints = ::readWMHints(blackbox, client.window);
+  client.wmnormal = ::readWMNormalHints(blackbox, client.window,
+                                        _screen->screenInfo());
+  client.wmprotocols = ::readWMProtocols(blackbox, client.window);
+  client.transient_for = ::readTransientInfo(blackbox, client.window,
+                                             _screen->screenInfo(),
+                                             client.wmhints);
+
+  if (client.wmhints.window_group != None)
+    (void) ::update_window_group(client.wmhints.window_group, blackbox, this);
+
+  if (isTransient()) {
+    // add ourselves to our transient_for
+    BlackboxWindow *win = findTransientFor();
+    if (win) {
+      win->addTransient(this);
+      client.ewmh.workspace = win->workspace();
+      setLayer(win->layer());
+    } else if (isGroupTransient()) {
+      BWindowGroup *group = findWindowGroup();
+      if (group)
+        group->addTransient(this);
+    } else {
+      // broken client
+      client.transient_for = 0;
+    }
+  }
+
+  client.state.visible = false;
+  client.state.iconic = false;
+  client.state.moving = false;
+  client.state.resizing = false;
+  client.state.focused = false;
+
+  switch (windowType()) {
+  case WindowTypeDesktop:
+    setLayer(StackingList::LayerDesktop);
+    break;
+
+  case WindowTypeDock:
+    setLayer(StackingList::LayerAbove);
+    // fallthrough intended
+
+  default:
+    if (client.ewmh.above)
+      setLayer(StackingList::LayerAbove);
+    else if (client.ewmh.below)
+      setLayer(StackingList::LayerBelow);
+    break;
+  } // switch
+
+  ::update_decorations(client.decorations,
+                       client.functions,
+                       isTransient(),
+                       client.ewmh,
+                       client.motif,
+                       client.wmnormal,
+                       client.wmprotocols);
+
+  // sanity checks
+  if (client.wmhints.initial_state == IconicState
+      && !hasWindowFunction(WindowFunctionIconify))
+    client.wmhints.initial_state = NormalState;
+  if (isMaximized() && !hasWindowFunction(WindowFunctionMaximize))
+    client.ewmh.maxv = client.ewmh.maxh = false;
+  if (isFullScreen() && !hasWindowFunction(WindowFunctionFullScreen))
+    client.ewmh.fullscreen = false;
+
+  bt::EWMH::Strut strut;
+  if (blackbox->ewmh().readWMStrut(client.window, &strut)) {
+    client.strut = new bt::EWMH::Strut;
+    *client.strut = strut;
+    _screen->addStrut(client.strut);
+  }
+
+  /*
+    if we just managed the group leader for an existing group, move
+    all group transients to this window
+  */
+  {
+    BWindowGroup *group = blackbox->findWindowGroup(client.window);
+    if (group) {
+      BlackboxWindowList transientList = group->transients();
+      BlackboxWindowList::const_iterator it = transientList.begin();
+      const BlackboxWindowList::const_iterator end = transientList.end();
+      for (; it != end; ++it) {
+        BlackboxWindow * const w1 = *it;
+        if (w1->client.transient_for != client.window)
+          continue;
+        group->removeTransient(w1);
+        addTransient(w1);
+        w1->changeWorkspace(workspace());
+        w1->changeLayer(layer());
+      }
+    }
+  }
+
+  frame.window = createToplevelWindow();
+  blackbox->insertEventHandler(frame.window, this);
+
+  frame.plate = createChildWindow(frame.window, NoEventMask);
+  blackbox->insertEventHandler(frame.plate, this);
+
+  if (client.decorations & WindowDecorationTitlebar)
+    createTitlebar();
+
+  if (client.decorations & WindowDecorationHandle)
+    createHandle();
+
+  // apply the size and gravity to the frame
+  const WindowStyle &style = _screen->resource().windowStyle();
+  frame.margin = ::update_margin(client.decorations, style);
+  frame.rect = ::applyGravity(client.rect,
+                              frame.margin,
+                              client.wmnormal.win_gravity);
+
+  associateClientWindow();
+
+  blackbox->insertEventHandler(client.window, this);
+  blackbox->insertWindow(client.window, this);
+  blackbox->insertWindow(frame.plate, this);
+
+  // preserve the window's initial state on first map, and its current
+  // state across a restart
+  if (!readState(client.current_state, blackbox, client.window))
+    client.current_state = client.wmhints.initial_state;
+
+  if (client.state.iconic) {
+    // prepare the window to be iconified
+    client.current_state = IconicState;
+    client.state.iconic = False;
+  } else if (workspace() != bt::BSENTINEL &&
+             workspace() != _screen->currentWorkspace()) {
+    client.current_state = WithdrawnState;
+  }
+
+  blackbox->XUngrabServer();
+
+  grabButtons();
+
+  XMapSubwindows(blackbox->XDisplay(), frame.window);
+
+  if (isFullScreen()) {
+    client.ewmh.fullscreen = false; // trick setFullScreen into working
+    setFullScreen(true);
+  } else {
+    if (isMaximized()) {
+      remaximize();
+    } else {
+      const unsigned long save_state = client.current_state;
+
+      bt::Rect r = frame.rect;
+      // trick configure into working
+      frame.rect = bt::Rect();
+      configure(r);
+
+      if (isShaded()) {
+        client.ewmh.shaded = false;
+        setShaded(true);
+      }
+
+      if (isShaded() && save_state != IconicState) {
+        /*
+          At this point in the life of a window, current_state should
+          only be set to IconicState if the window was an *icon*, not
+          if it was shaded.
+        */
+        client.current_state = save_state;
+      }
+    }
+  }
+}
+
+
+BlackboxWindow::~BlackboxWindow(void) {
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  if (! timer) // window not managed...
+    return;
+
+  if (client.state.moving || client.state.resizing) {
+    _screen->hideGeometry();
+    XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
+  }
+
+  delete timer;
+
+  if (client.strut) {
+    _screen->removeStrut(client.strut);
+    delete client.strut;
+  }
+
+  BWindowGroup *group = findWindowGroup();
+
+  if (isTransient()) {
+    // remove ourselves from our transient_for
+    BlackboxWindow *win = findTransientFor();
+    if (win) {
+      win->removeTransient(this);
+    } else if (isGroupTransient()) {
+      if (group)
+        group->removeTransient(this);
+    }
+    client.transient_for = 0;
+  }
+
+  if (group)
+    group->removeWindow(this);
+
+  if (frame.title)
+    destroyTitlebar();
+
+  if (frame.handle)
+    destroyHandle();
+
+  blackbox->removeEventHandler(client.window);
+  blackbox->removeWindow(client.window);
+
+  blackbox->removeEventHandler(frame.plate);
+  blackbox->removeWindow(frame.plate);
+  XDestroyWindow(blackbox->XDisplay(), frame.plate);
+
+  blackbox->removeEventHandler(frame.window);
+  XDestroyWindow(blackbox->XDisplay(), frame.window);
+}
+
+
+/*
+ * Creates a new top level window, with a given location, size, and border
+ * width.
+ * Returns: the newly created window
+ */
+Window BlackboxWindow::createToplevelWindow(void) {
+  XSetWindowAttributes attrib_create;
+  unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
+
+  attrib_create.colormap = _screen->screenInfo().colormap();
+  attrib_create.override_redirect = True;
+  attrib_create.event_mask = EnterWindowMask | LeaveWindowMask;
+
+  return XCreateWindow(blackbox->XDisplay(),
+                       _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
+                       _screen->screenInfo().depth(), InputOutput,
+                       _screen->screenInfo().visual(),
+                       create_mask, &attrib_create);
+}
+
+
+/*
+ * Creates a child window, and optionally associates a given cursor with
+ * the new window.
+ */
+Window BlackboxWindow::createChildWindow(Window parent,
+                                         unsigned long event_mask,
+                                         Cursor cursor) {
+  XSetWindowAttributes attrib_create;
+  unsigned long create_mask = CWEventMask;
+
+  attrib_create.event_mask = event_mask;
+
+  if (cursor) {
+    create_mask |= CWCursor;
+    attrib_create.cursor = cursor;
+  }
+
+  return XCreateWindow(blackbox->XDisplay(), parent, 0, 0, 1, 1, 0,
+                       _screen->screenInfo().depth(), InputOutput,
+                       _screen->screenInfo().visual(),
+                       create_mask, &attrib_create);
+}
+
+
+/*
+ * Reparents the client window into the newly created frame.
+ *
+ * Note: the server must be grabbed before calling this function.
+ */
+void BlackboxWindow::associateClientWindow(void) {
+  XSetWindowBorderWidth(blackbox->XDisplay(), client.window, 0);
+  XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeInsert);
+
+  XSelectInput(blackbox->XDisplay(), frame.plate,
+               FocusChangeMask | SubstructureRedirectMask);
+
+  XSelectInput(blackbox->XDisplay(), client.window,
+               client_window_event_mask & ~StructureNotifyMask);
+  XReparentWindow(blackbox->XDisplay(), client.window, frame.plate, 0, 0);
+  XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
+
+#ifdef    SHAPE
+  if (blackbox->hasShapeExtensions()) {
+    XShapeSelectInput(blackbox->XDisplay(), client.window,
+                      ShapeNotifyMask);
+
+    Bool shaped = False;
+    int foo;
+    unsigned int ufoo;
+
+    XShapeQueryExtents(blackbox->XDisplay(), client.window, &shaped,
+                       &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
+                       &ufoo, &ufoo);
+    client.state.shaped = shaped;
+  }
+#endif // SHAPE
+}
+
+
+void BlackboxWindow::decorate(void) {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  if (client.decorations & WindowDecorationTitlebar) {
+    // render focused button texture
+    frame.fbutton =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.focus.button,
+                            style.button_width,
+                            style.button_width,
+                            frame.fbutton);
+
+    // render unfocused button texture
+    frame.ubutton =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.unfocus.button,
+                            style.button_width,
+                            style.button_width,
+                            frame.ubutton);
+
+    // render pressed button texture
+    frame.pbutton =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.pressed,
+                            style.button_width,
+                            style.button_width,
+                            frame.pbutton);
+
+    // render focused titlebar texture
+    frame.ftitle =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.focus.title,
+                            frame.rect.width(),
+                            style.title_height,
+                            frame.ftitle);
+
+    // render unfocused titlebar texture
+    frame.utitle =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.unfocus.title,
+                            frame.rect.width(),
+                            style.title_height,
+                            frame.utitle);
+
+    // render focused label texture
+    frame.flabel =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.focus.label,
+                            frame.label_w,
+                            style.label_height,
+                            frame.flabel);
+
+    // render unfocused label texture
+    frame.ulabel =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.unfocus.label,
+                            frame.label_w,
+                            style.label_height,
+                            frame.ulabel);
+  }
+
+  if (client.decorations & WindowDecorationHandle) {
+    frame.fhandle =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.focus.handle,
+                            frame.rect.width(),
+                            style.handle_height,
+                            frame.fhandle);
+
+    frame.uhandle =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.unfocus.handle,
+                            frame.rect.width(),
+                            style.handle_height,
+                            frame.uhandle);
+  }
+
+  if (client.decorations & WindowDecorationGrip) {
+    frame.fgrip =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.focus.grip,
+                            style.grip_width,
+                            style.handle_height,
+                            frame.fgrip);
+
+    frame.ugrip =
+      bt::PixmapCache::find(_screen->screenNumber(),
+                            style.unfocus.grip,
+                            style.grip_width,
+                            style.handle_height,
+                            frame.ugrip);
+  }
+}
+
+
+void BlackboxWindow::createHandle(void) {
+  frame.handle = createChildWindow(frame.window,
+                                   ButtonPressMask | ButtonReleaseMask |
+                                   ButtonMotionMask | ExposureMask);
+  blackbox->insertEventHandler(frame.handle, this);
+
+  if (client.decorations & WindowDecorationGrip)
+    createGrips();
+}
+
+
+void BlackboxWindow::destroyHandle(void) {
+  if (frame.left_grip || frame.right_grip)
+    destroyGrips();
+
+  if (frame.fhandle) bt::PixmapCache::release(frame.fhandle);
+  if (frame.uhandle) bt::PixmapCache::release(frame.uhandle);
+
+  frame.fhandle = frame.uhandle = None;
+
+  blackbox->removeEventHandler(frame.handle);
+  XDestroyWindow(blackbox->XDisplay(), frame.handle);
+  frame.handle = None;
+}
+
+
+void BlackboxWindow::createGrips(void) {
+  frame.left_grip =
+    createChildWindow(frame.handle,
+                      ButtonPressMask | ButtonReleaseMask |
+                      ButtonMotionMask | ExposureMask,
+                      blackbox->resource().cursors().resize_bottom_left);
+  blackbox->insertEventHandler(frame.left_grip, this);
+
+  frame.right_grip =
+    createChildWindow(frame.handle,
+                      ButtonPressMask | ButtonReleaseMask |
+                      ButtonMotionMask | ExposureMask,
+                      blackbox->resource().cursors().resize_bottom_right);
+  blackbox->insertEventHandler(frame.right_grip, this);
+}
+
+
+void BlackboxWindow::destroyGrips(void) {
+  if (frame.fgrip) bt::PixmapCache::release(frame.fgrip);
+  if (frame.ugrip) bt::PixmapCache::release(frame.ugrip);
+
+  frame.fgrip = frame.ugrip = None;
+
+  blackbox->removeEventHandler(frame.left_grip);
+  blackbox->removeEventHandler(frame.right_grip);
+
+  XDestroyWindow(blackbox->XDisplay(), frame.left_grip);
+  XDestroyWindow(blackbox->XDisplay(), frame.right_grip);
+  frame.left_grip = frame.right_grip = None;
+}
+
+
+void BlackboxWindow::createTitlebar(void) {
+  frame.title = createChildWindow(frame.window,
+                                  ButtonPressMask | ButtonReleaseMask |
+                                  ButtonMotionMask | ExposureMask);
+  frame.label = createChildWindow(frame.title,
+                                  ButtonPressMask | ButtonReleaseMask |
+                                  ButtonMotionMask | ExposureMask);
+  blackbox->insertEventHandler(frame.title, this);
+  blackbox->insertEventHandler(frame.label, this);
+
+  if (client.decorations & WindowDecorationIconify) createIconifyButton();
+  if (client.decorations & WindowDecorationMaximize) createMaximizeButton();
+  if (client.decorations & WindowDecorationClose) createCloseButton();
+}
+
+
+void BlackboxWindow::destroyTitlebar(void) {
+  if (frame.close_button)
+    destroyCloseButton();
+
+  if (frame.iconify_button)
+    destroyIconifyButton();
+
+  if (frame.maximize_button)
+    destroyMaximizeButton();
+
+  if (frame.fbutton) bt::PixmapCache::release(frame.fbutton);
+  if (frame.ubutton) bt::PixmapCache::release(frame.ubutton);
+  if (frame.pbutton) bt::PixmapCache::release(frame.pbutton);
+  if (frame.ftitle)  bt::PixmapCache::release(frame.ftitle);
+  if (frame.utitle)  bt::PixmapCache::release(frame.utitle);
+  if (frame.flabel)  bt::PixmapCache::release(frame.flabel);
+  if (frame.ulabel)  bt::PixmapCache::release(frame.ulabel);
+
+  frame.fbutton = frame.ubutton = frame.pbutton =
+   frame.ftitle = frame.utitle =
+   frame.flabel = frame.ulabel = None;
+
+  blackbox->removeEventHandler(frame.title);
+  blackbox->removeEventHandler(frame.label);
+
+  XDestroyWindow(blackbox->XDisplay(), frame.label);
+  XDestroyWindow(blackbox->XDisplay(), frame.title);
+  frame.title = frame.label = None;
+}
+
+
+void BlackboxWindow::createCloseButton(void) {
+  if (frame.title != None) {
+    frame.close_button = createChildWindow(frame.title,
+                                           ButtonPressMask |
+                                           ButtonReleaseMask |
+                                           ButtonMotionMask | ExposureMask);
+    blackbox->insertEventHandler(frame.close_button, this);
+  }
+}
+
+
+void BlackboxWindow::destroyCloseButton(void) {
+  blackbox->removeEventHandler(frame.close_button);
+  XDestroyWindow(blackbox->XDisplay(), frame.close_button);
+  frame.close_button = None;
+}
+
+
+void BlackboxWindow::createIconifyButton(void) {
+  if (frame.title != None) {
+    frame.iconify_button = createChildWindow(frame.title,
+                                             ButtonPressMask |
+                                             ButtonReleaseMask |
+                                             ButtonMotionMask | ExposureMask);
+    blackbox->insertEventHandler(frame.iconify_button, this);
+  }
+}
+
+
+void BlackboxWindow::destroyIconifyButton(void) {
+  blackbox->removeEventHandler(frame.iconify_button);
+  XDestroyWindow(blackbox->XDisplay(), frame.iconify_button);
+  frame.iconify_button = None;
+}
+
+
+void BlackboxWindow::createMaximizeButton(void) {
+  if (frame.title != None) {
+    frame.maximize_button = createChildWindow(frame.title,
+                                              ButtonPressMask |
+                                              ButtonReleaseMask |
+                                              ButtonMotionMask | ExposureMask);
+    blackbox->insertEventHandler(frame.maximize_button, this);
+  }
+}
+
+
+void BlackboxWindow::destroyMaximizeButton(void) {
+  blackbox->removeEventHandler(frame.maximize_button);
+  XDestroyWindow(blackbox->XDisplay(), frame.maximize_button);
+  frame.maximize_button = None;
+}
+
+
+void BlackboxWindow::positionButtons(bool redecorate_label) {
+  // we need to use signed ints here to detect windows that are too small
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const int extra = style.title_margin == 0 ?
+                    style.focus.button.borderWidth() : 0,
+               bw = style.button_width + style.title_margin
+                    - extra,
+               by = style.title_margin +
+                    style.focus.title.borderWidth();
+  int lx = by, lw = frame.rect.width() - by;
+
+  if (client.decorations & WindowDecorationIconify) {
+    if (frame.iconify_button == None) createIconifyButton();
+
+    XMoveResizeWindow(blackbox->XDisplay(), frame.iconify_button, by, by,
+                      style.button_width, style.button_width);
+    XMapWindow(blackbox->XDisplay(), frame.iconify_button);
+
+    lx += bw;
+    lw -= bw;
+  } else if (frame.iconify_button) {
+    destroyIconifyButton();
+  }
+
+  int bx = frame.rect.width() - bw
+           - style.focus.title.borderWidth() - extra;
+
+  if (client.decorations & WindowDecorationClose) {
+    if (frame.close_button == None) createCloseButton();
+
+    XMoveResizeWindow(blackbox->XDisplay(), frame.close_button, bx, by,
+                      style.button_width, style.button_width);
+    XMapWindow(blackbox->XDisplay(), frame.close_button);
+
+    bx -= bw;
+    lw -= bw;
+  } else if (frame.close_button) {
+    destroyCloseButton();
+  }
+
+  if (client.decorations & WindowDecorationMaximize) {
+    if (frame.maximize_button == None) createMaximizeButton();
+
+    XMoveResizeWindow(blackbox->XDisplay(), frame.maximize_button, bx, by,
+                      style.button_width, style.button_width);
+    XMapWindow(blackbox->XDisplay(), frame.maximize_button);
+
+    bx -= bw;
+    lw -= bw;
+  } else if (frame.maximize_button) {
+    destroyMaximizeButton();
+  }
+
+  if (lw > by) {
+    frame.label_w = lw - by;
+    XMoveResizeWindow(blackbox->XDisplay(), frame.label, lx, by,
+                      frame.label_w, style.label_height);
+    XMapWindow(blackbox->XDisplay(), frame.label);
+
+    if (redecorate_label) {
+      frame.flabel =
+        bt::PixmapCache::find(_screen->screenNumber(),
+                              style.focus.label,
+                              frame.label_w, style.label_height,
+                              frame.flabel);
+      frame.ulabel =
+        bt::PixmapCache::find(_screen->screenNumber(),
+                              style.unfocus.label,
+                              frame.label_w, style.label_height,
+                              frame.ulabel);
+    }
+
+    const bt::ustring ellided =
+      bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
+                     _screen->screenNumber(), style.font);
+
+    if (ellided != client.visible_title) {
+      client.visible_title = ellided;
+      blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
+    }
+  } else {
+    frame.label_w = 1;
+    XUnmapWindow(blackbox->XDisplay(), frame.label);
+  }
+
+  redrawLabel();
+  redrawAllButtons();
+}
+
+
+void BlackboxWindow::reconfigure(void) {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  if (isMaximized()) {
+    // update the frame margin in case the style has changed
+    frame.margin = ::update_margin(client.decorations, style);
+
+    // make sure maximized windows have the correct size after a style
+    // change
+    remaximize();
+  } else {
+    // get the client window geometry as if it was unmanaged
+    bt::Rect r = frame.rect;
+    if (client.ewmh.shaded) {
+      r.setHeight(client.rect.height() + frame.margin.top
+                  + frame.margin.bottom);
+    }
+    r = ::restoreGravity(r, frame.margin, client.wmnormal.win_gravity);
+
+    // update the frame margin in case the style has changed
+    frame.margin = ::update_margin(client.decorations, style);
+
+    // get the frame window geometry from the client window geometry
+    // calculated above
+    r = ::applyGravity(r, frame.margin, client.wmnormal.win_gravity);
+    if (client.ewmh.shaded) {
+      frame.rect = r;
+
+      positionWindows();
+      decorate();
+
+      // keep the window shaded
+      frame.rect.setHeight(style.title_height);
+      XResizeWindow(blackbox->XDisplay(), frame.window,
+                    frame.rect.width(), frame.rect.height());
+    } else {
+      // trick configure into working
+      frame.rect = bt::Rect();
+      configure(r);
+    }
+  }
+
+  ungrabButtons();
+  grabButtons();
+}
+
+
+void BlackboxWindow::grabButtons(void) {
+  if (blackbox->resource().focusModel() == ClickToFocusModel
+      || blackbox->resource().clickRaise())
+    // grab button 1 for changing focus/raising
+    blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
+                         GrabModeSync, GrabModeSync, frame.plate, None,
+                         blackbox->resource().allowScrollLock());
+
+  if (hasWindowFunction(WindowFunctionMove))
+    blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
+                         ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+                         GrabModeAsync, frame.window,
+                         blackbox->resource().cursors().move,
+                         blackbox->resource().allowScrollLock());
+  if (hasWindowFunction(WindowFunctionResize))
+    blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
+                         ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+                         GrabModeAsync, frame.window,
+                         None, blackbox->resource().allowScrollLock());
+  // alt+middle lowers the window
+  blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
+                       ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
+                       frame.window, None,
+                       blackbox->resource().allowScrollLock());
+  
+  blackbox->grabButton(Button3, Mod4Mask, frame.window, True,
+                       ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
+                       frame.window, None,
+                       blackbox->resource().allowScrollLock());
+}
+
+
+void BlackboxWindow::ungrabButtons(void) {
+  blackbox->ungrabButton(Button1, 0, frame.plate);
+  blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
+  blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
+  blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
+  blackbox->ungrabButton(Button3, Mod4Mask, frame.window);
+}
+
+
+void BlackboxWindow::positionWindows(void) {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const unsigned int bw = (hasWindowDecoration(WindowDecorationBorder)
+                           ? style.frame_border_width
+                           : 0);
+
+  XMoveResizeWindow(blackbox->XDisplay(), frame.plate,
+                    frame.margin.left - bw,
+                    frame.margin.top - bw,
+                    client.rect.width(), client.rect.height());
+  XSetWindowBorderWidth(blackbox->XDisplay(), frame.plate, bw);
+  XMoveResizeWindow(blackbox->XDisplay(), client.window,
+                    0, 0, client.rect.width(), client.rect.height());
+  // ensure client.rect contains the real location
+  client.rect.setPos(frame.rect.left() + frame.margin.left,
+                     frame.rect.top() + frame.margin.top);
+
+  if (client.decorations & WindowDecorationTitlebar) {
+    if (frame.title == None) createTitlebar();
+
+    XMoveResizeWindow(blackbox->XDisplay(), frame.title,
+                      0, 0, frame.rect.width(), style.title_height);
+
+    positionButtons();
+    XMapSubwindows(blackbox->XDisplay(), frame.title);
+    XMapWindow(blackbox->XDisplay(), frame.title);
+  } else if (frame.title) {
+    destroyTitlebar();
+  }
+
+  if (client.decorations & WindowDecorationHandle) {
+    if (frame.handle == None) createHandle();
+
+    // use client.rect here so the value is correct even if shaded
+    XMoveResizeWindow(blackbox->XDisplay(), frame.handle,
+                      0, client.rect.height() + frame.margin.top,
+                      frame.rect.width(), style.handle_height);
+
+    if (client.decorations & WindowDecorationGrip) {
+      if (frame.left_grip == None || frame.right_grip == None) createGrips();
+
+      XMoveResizeWindow(blackbox->XDisplay(), frame.left_grip, 0, 0,
+                        style.grip_width, style.handle_height);
+
+      const int nx = frame.rect.width() - style.grip_width;
+      XMoveResizeWindow(blackbox->XDisplay(), frame.right_grip, nx, 0,
+                        style.grip_width, style.handle_height);
+
+      XMapSubwindows(blackbox->XDisplay(), frame.handle);
+    } else {
+      destroyGrips();
+    }
+
+    XMapWindow(blackbox->XDisplay(), frame.handle);
+  } else if (frame.handle) {
+    destroyHandle();
+  }
+}
+
+
+/*
+ * This function is responsible for updating both the client and the
+ * frame rectangles.  According to the ICCCM a client message is not
+ * sent for a resize, only a move.
+ */
+void BlackboxWindow::configure(int dx, int dy,
+                               unsigned int dw, unsigned int dh) {
+  bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
+                     ! client.state.moving);
+
+  if (dw != frame.rect.width() || dh != frame.rect.height()) {
+    frame.rect.setRect(dx, dy, dw, dh);
+
+    if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
+      frame.rect.setPos(0, 0);
+
+    client.rect.setCoords(frame.rect.left() + frame.margin.left,
+                          frame.rect.top() + frame.margin.top,
+                          frame.rect.right() - frame.margin.right,
+                          frame.rect.bottom() - frame.margin.bottom);
+
+#ifdef SHAPE
+    if (client.state.shaped)
+      configureShape();
+#endif // SHAPE
+
+    XMoveResizeWindow(blackbox->XDisplay(), frame.window,
+                      frame.rect.x(), frame.rect.y(),
+                      frame.rect.width(), frame.rect.height());
+
+    positionWindows();
+    decorate();
+    redrawWindowFrame();
+  } else {
+    frame.rect.setPos(dx, dy);
+
+    XMoveWindow(blackbox->XDisplay(), frame.window,
+                frame.rect.x(), frame.rect.y());
+    /*
+      we may have been called just after an opaque window move, so
+      even though the old coords match the new ones no ConfigureNotify
+      has been sent yet.  There are likely other times when this will
+      be relevant as well.
+    */
+    if (! client.state.moving) send_event = True;
+  }
+
+  if (send_event) {
+    // if moving, the update and event will occur when the move finishes
+    client.rect.setPos(frame.rect.left() + frame.margin.left,
+                       frame.rect.top() + frame.margin.top);
+
+    XEvent event;
+    event.type = ConfigureNotify;
+
+    event.xconfigure.display = blackbox->XDisplay();
+    event.xconfigure.event = client.window;
+    event.xconfigure.window = client.window;
+    event.xconfigure.x = client.rect.x();
+    event.xconfigure.y = client.rect.y();
+    event.xconfigure.width = client.rect.width();
+    event.xconfigure.height = client.rect.height();
+    event.xconfigure.border_width = client.old_bw;
+    event.xconfigure.above = frame.window;
+    event.xconfigure.override_redirect = False;
+
+    XSendEvent(blackbox->XDisplay(), client.window, False,
+               StructureNotifyMask, &event);
+  }
+}
+
+
+#ifdef SHAPE
+void BlackboxWindow::configureShape(void) {
+  XShapeCombineShape(blackbox->XDisplay(), frame.window, ShapeBounding,
+                     frame.margin.left, frame.margin.top,
+                     client.window, ShapeBounding, ShapeSet);
+
+  int num = 0;
+  XRectangle xrect[2];
+
+  const WindowStyle &style = _screen->resource().windowStyle();
+  if (client.decorations & WindowDecorationTitlebar) {
+    xrect[0].x = xrect[0].y = 0;
+    xrect[0].width = frame.rect.width();
+    xrect[0].height = style.title_height;
+    ++num;
+  }
+
+  if (client.decorations & WindowDecorationHandle) {
+    xrect[1].x = 0;
+    xrect[1].y = client.rect.height() + frame.margin.top;
+    xrect[1].width = frame.rect.width();
+    xrect[1].height = style.handle_height;
+    ++num;
+  }
+
+  XShapeCombineRectangles(blackbox->XDisplay(), frame.window,
+                          ShapeBounding, 0, 0, xrect, num,
+                          ShapeUnion, Unsorted);
+}
+#endif // SHAPE
+
+
+void BlackboxWindow::addTransient(BlackboxWindow *win)
+{ client.transientList.push_front(win); }
+
+
+void BlackboxWindow::removeTransient(BlackboxWindow *win)
+{ client.transientList.remove(win); }
+
+
+BlackboxWindow *BlackboxWindow::findTransientFor(void) const {
+  BlackboxWindow *win = 0;
+  if (isTransient()) {
+    win = blackbox->findWindow(client.transient_for);
+    if (win && win->_screen != _screen)
+      win = 0;
+  }
+  return win;
+}
+
+
+/*
+  walk up to either 1) a non-transient window 2) a group transient,
+  watching out for a circular chain
+
+  this function returns zero for non-transient windows
+*/
+BlackboxWindow *BlackboxWindow::findNonTransientParent(void) const {
+  BlackboxWindowList seen;
+  seen.push_back(const_cast<BlackboxWindow *>(this));
+
+  BlackboxWindow *w = findTransientFor();
+  if (!w)
+    return 0;
+
+  while (w->isTransient() && !w->isGroupTransient()) {
+    seen.push_back(w);
+    BlackboxWindow * const tmp = w->findTransientFor();
+    if (!tmp)
+      break;
+    if (std::find(seen.begin(), seen.end(), tmp) != seen.end()) {
+      // circular transient chain
+      break;
+    }
+    w = tmp;
+  }
+  return w;
+}
+
+
+/*
+  Returns a list of all transients.  This is recursive, so it returns
+  all transients of transients as well.
+*/
+BlackboxWindowList BlackboxWindow::buildFullTransientList(void) const {
+  BlackboxWindowList all = client.transientList;
+  BlackboxWindowList::const_iterator it = client.transientList.begin(),
+                                    end = client.transientList.end();
+  for (; it != end; ++it) {
+    BlackboxWindowList x = (*it)->buildFullTransientList();
+    all.splice(all.end(), x);
+  }
+  return all;
+}
+
+
+BWindowGroup *BlackboxWindow::findWindowGroup(void) const {
+  BWindowGroup *group = 0;
+  if (client.wmhints.window_group)
+    group = blackbox->findWindowGroup(client.wmhints.window_group);
+  return group;
+}
+
+
+void BlackboxWindow::setWorkspace(unsigned int new_workspace) {
+  client.ewmh.workspace = new_workspace;
+  blackbox->ewmh().setWMDesktop(client.window, client.ewmh.workspace);
+}
+
+
+void BlackboxWindow::changeWorkspace(unsigned int new_workspace,
+                                     ChangeWorkspaceOption how) {
+  if (client.ewmh.workspace == new_workspace)
+    return;
+
+  if (isTransient()) {
+    BlackboxWindow *win = findTransientFor();
+    if (win) {
+      if (win->workspace() != new_workspace) {
+        win->changeWorkspace(new_workspace, how);
+        return;
+      }
+    }
+  } else {
+    assert(hasWindowFunction(WindowFunctionChangeWorkspace));
+  }
+
+  Workspace *ws;
+  if (workspace() != bt::BSENTINEL) {
+    ws = _screen->findWorkspace(workspace());
+    assert(ws != 0);
+    ws->removeWindow(this);
+  }
+
+  if (new_workspace != bt::BSENTINEL) {
+    ws = _screen->findWorkspace(new_workspace);
+    assert(ws != 0);
+    ws->addWindow(this);
+  }
+
+  switch (how) {
+  case StayOnCurrentWorkspace:
+    if (isVisible() && workspace() != bt::BSENTINEL
+        && workspace() != _screen->currentWorkspace()) {
+      hide();
+    } else if (!isVisible()
+               && (workspace() == bt::BSENTINEL
+                   || workspace() == _screen->currentWorkspace())) {
+      show();
+    }
+    break;
+
+  case SwitchToNewWorkspace:
+    /*
+      we will change to the new workspace soon, so force this window
+      to be visible
+    */
+    show();
+    break;
+  }
+
+  // change workspace on all transients
+  if (!client.transientList.empty()) {
+    BlackboxWindowList::iterator it = client.transientList.begin(),
+                                end = client.transientList.end();
+    for (; it != end; ++it)
+      (*it)->changeWorkspace(new_workspace, how);
+  }
+}
+
+
+void BlackboxWindow::changeLayer(StackingList::Layer new_layer) {
+  if (layer() == new_layer)
+    return;
+
+  bool restack = false;
+  if (isTransient()) {
+    BlackboxWindow *win = findTransientFor();
+    if (win) {
+      if (win->layer() != new_layer) {
+        win->changeLayer(new_layer);
+        return;
+      } else {
+        restack = true;
+      }
+    }
+  } else {
+    assert(hasWindowFunction(WindowFunctionChangeLayer));
+    restack = true;
+  }
+
+  _screen->stackingList().changeLayer(this, new_layer);
+
+  if (!client.transientList.empty()) {
+    BlackboxWindowList::iterator it = client.transientList.begin();
+    const BlackboxWindowList::iterator end = client.transientList.end();
+    for (; it != end; ++it)
+      (*it)->changeLayer(new_layer);
+  }
+
+  if (restack)
+    _screen->restackWindows();
+}
+
+
+bool BlackboxWindow::setInputFocus(void) {
+  if (!isVisible())
+    return false;
+  if (client.state.focused)
+    return true;
+
+  switch (windowType()) {
+  case WindowTypeDock:
+    /*
+      Many docks have auto-hide features similar to the toolbar and
+      slit... we don't want these things to be moved to the center of
+      the screen when switching to an empty workspace.
+    */
+    break;
+
+  default:
+    {  const bt::Rect &scr = _screen->screenInfo().rect();
+      if (!frame.rect.intersects(scr)) {
+        // client is outside the screen, move it to the center
+        configure(scr.x() + (scr.width() - frame.rect.width()) / 2,
+                  scr.y() + (scr.height() - frame.rect.height()) / 2,
+                  frame.rect.width(), frame.rect.height());
+      }
+      break;
+    }
+  }
+
+  /*
+    pass focus to any modal transients, giving modal group transients
+    higher priority
+  */
+  BWindowGroup *group = findWindowGroup();
+  if (group && !group->transients().empty()) {
+    BlackboxWindowList::const_iterator it = group->transients().begin(),
+                                      end = group->transients().end();
+    for (; it != end; ++it) {
+      BlackboxWindow * const tmp = *it;
+      if (!tmp->isVisible() || !tmp->isModal())
+        continue;
+      if (tmp == this) {
+        // we are the newest modal group transient
+        break;
+      }
+      if (isTransient()) {
+        if (tmp == findNonTransientParent()) {
+          // we are a transient of the modal group transient
+          break;
+        }
+      }
+      return tmp->setInputFocus();
+    }
+  }
+
+  if (!client.transientList.empty()) {
+    BlackboxWindowList::const_iterator it = client.transientList.begin(),
+                                      end = client.transientList.end();
+    for (; it != end; ++it) {
+      BlackboxWindow * const tmp = *it;
+      if (tmp->isVisible() && tmp->isModal())
+        return tmp->setInputFocus();
+    }
+  }
+
+  switch (windowType()) {
+  case WindowTypeDock:
+    return false;
+  default:
+    break;
+  }
+
+  XSetInputFocus(blackbox->XDisplay(), client.window,
+                 RevertToPointerRoot, blackbox->XTime());
+
+  if (client.wmprotocols.wm_take_focus) {
+    XEvent ce;
+    ce.xclient.type = ClientMessage;
+    ce.xclient.message_type = blackbox->wmProtocolsAtom();
+    ce.xclient.display = blackbox->XDisplay();
+    ce.xclient.window = client.window;
+    ce.xclient.format = 32;
+    ce.xclient.data.l[0] = blackbox->wmTakeFocusAtom();
+    ce.xclient.data.l[1] = blackbox->XTime();
+    ce.xclient.data.l[2] = 0l;
+    ce.xclient.data.l[3] = 0l;
+    ce.xclient.data.l[4] = 0l;
+    XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
+  }
+
+  return true;
+}
+
+
+void BlackboxWindow::show(void) {
+  if (client.state.visible)
+    return;
+
+  if (client.state.iconic)
+    _screen->removeIcon(this);
+
+  client.state.iconic = false;
+  client.state.visible = true;
+  setState(isShaded() ? IconicState : NormalState);
+
+  XMapWindow(blackbox->XDisplay(), client.window);
+  XMapSubwindows(blackbox->XDisplay(), frame.window);
+  XMapWindow(blackbox->XDisplay(), frame.window);
+
+  if (!client.transientList.empty()) {
+    BlackboxWindowList::iterator it = client.transientList.begin(),
+                                end = client.transientList.end();
+    for (; it != end; ++it)
+      (*it)->show();
+  }
+
+#ifdef DEBUG
+  int real_x, real_y;
+  Window child;
+  XTranslateCoordinates(blackbox->XDisplay(), client.window,
+                        _screen->screenInfo().rootWindow(),
+                        0, 0, &real_x, &real_y, &child);
+  fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", title().c_str(),
+          client.rect.left(), client.rect.top(), real_x, real_y);
+  assert(client.rect.left() == real_x && client.rect.top() == real_y);
+#endif
+}
+
+
+void BlackboxWindow::hide(void) {
+  if (!client.state.visible)
+    return;
+
+  client.state.visible = false;
+  setState(client.state.iconic ? IconicState : client.current_state);
+
+  XUnmapWindow(blackbox->XDisplay(), frame.window);
+
+  /*
+   * we don't want this XUnmapWindow call to generate an UnmapNotify
+   * event, so we need to clear the event mask on client.window for a
+   * split second.  HOWEVER, since X11 is asynchronous, the window
+   * could be destroyed in that split second, leaving us with a ghost
+   * window... so, we need to do this while the X server is grabbed
+   */
+  blackbox->XGrabServer();
+  XSelectInput(blackbox->XDisplay(), client.window,
+               client_window_event_mask & ~StructureNotifyMask);
+  XUnmapWindow(blackbox->XDisplay(), client.window);
+  XSelectInput(blackbox->XDisplay(), client.window, client_window_event_mask);
+  blackbox->XUngrabServer();
+}
+
+
+void BlackboxWindow::close(void) {
+  assert(hasWindowFunction(WindowFunctionClose));
+
+  XEvent ce;
+  ce.xclient.type = ClientMessage;
+  ce.xclient.message_type = blackbox->wmProtocolsAtom();
+  ce.xclient.display = blackbox->XDisplay();
+  ce.xclient.window = client.window;
+  ce.xclient.format = 32;
+  ce.xclient.data.l[0] = blackbox->wmDeleteWindowAtom();
+  ce.xclient.data.l[1] = blackbox->XTime();
+  ce.xclient.data.l[2] = 0l;
+  ce.xclient.data.l[3] = 0l;
+  ce.xclient.data.l[4] = 0l;
+  XSendEvent(blackbox->XDisplay(), client.window, False, NoEventMask, &ce);
+}
+
+
+void BlackboxWindow::activate(void) {
+  if (workspace() != bt::BSENTINEL
+      && workspace() != _screen->currentWorkspace())
+    _screen->setCurrentWorkspace(workspace());
+  if (client.state.iconic)
+    show();
+  if (client.ewmh.shaded)
+    setShaded(false);
+  if (setInputFocus())
+    _screen->raiseWindow(this);
+}
+
+
+void BlackboxWindow::iconify(void) {
+  if (client.state.iconic)
+    return;
+
+  if (isTransient()) {
+    BlackboxWindow *win = findTransientFor();
+    if (win) {
+      if (!win->isIconic()) {
+        win->iconify();
+        return;
+      }
+    }
+  } else {
+    assert(hasWindowFunction(WindowFunctionIconify));
+  }
+
+  _screen->addIcon(this);
+
+  client.state.iconic = true;
+  hide();
+
+  // iconify all transients
+  if (!client.transientList.empty()) {
+    BlackboxWindowList::iterator it = client.transientList.begin(),
+                                end = client.transientList.end();
+    for (; it != end; ++it)
+      (*it)->iconify();
+  }
+}
+
+
+void BlackboxWindow::maximize(unsigned int button) {
+  assert(hasWindowFunction(WindowFunctionMaximize));
+
+  // any maximize operation always unshades
+  client.ewmh.shaded = false;
+  frame.rect.setHeight(client.rect.height() + frame.margin.top
+                       + frame.margin.bottom);
+
+  if (isMaximized()) {
+    // restore from maximized
+    client.ewmh.maxh = client.ewmh.maxv = false;
+
+    if (!isFullScreen()) {
+      /*
+        when a resize is begun, maximize(0) is called to clear any
+        maximization flags currently set.  Otherwise it still thinks
+        it is maximized.  so we do not need to call configure()
+        because resizing will handle it
+      */
+      if (! client.state.resizing) {
+        bt::Rect r = ::applyGravity(client.premax,
+                                    frame.margin,
+                                    client.wmnormal.win_gravity);
+        r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
+        // trick configure into working
+        frame.rect = bt::Rect();
+        configure(r);
+      }
+
+      redrawAllButtons(); // in case it is not called in configure()
+    }
+
+    updateEWMHState();
+    updateEWMHAllowedActions();
+    return;
+  }
+
+  switch (button) {
+  case 1:
+    client.ewmh.maxh = true;
+    client.ewmh.maxv = true;
+    break;
+
+  case 2:
+    client.ewmh.maxh = false;
+    client.ewmh.maxv = true;
+    break;
+
+  case 3:
+    client.ewmh.maxh = true;
+    client.ewmh.maxv = false;
+    break;
+
+  default:
+    assert(0);
+    break;
+  }
+
+  if (!isFullScreen()) {
+    // go go gadget-maximize!
+    bt::Rect r = _screen->availableArea();
+
+    if (!client.ewmh.maxh) {
+      r.setX(frame.rect.x());
+      r.setWidth(frame.rect.width());
+    }
+    if (!client.ewmh.maxv) {
+      r.setY(frame.rect.y());
+      r.setHeight(frame.rect.height());
+    }
+
+    // store the current frame geometry, so that we can restore it later
+    client.premax = ::restoreGravity(frame.rect,
+                                     frame.margin,
+                                     client.wmnormal.win_gravity);
+
+    r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
+    // trick configure into working
+    frame.rect = bt::Rect();
+    configure(r);
+  }
+
+  updateEWMHState();
+  updateEWMHAllowedActions();
+}
+
+
+/*
+  re-maximizes the window to take into account availableArea changes.
+
+  note that unlike maximize(), the shaded state is preserved.
+*/
+void BlackboxWindow::remaximize(void) {
+  if (isShaded()) {
+    bt::Rect r = _screen->availableArea();
+
+    if (!client.ewmh.maxh) {
+      r.setX(frame.rect.x());
+      r.setWidth(frame.rect.width());
+    }
+    if (!client.ewmh.maxv) {
+      r.setY(frame.rect.y());
+      r.setHeight(frame.rect.height());
+    }
+
+    frame.rect = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
+
+    positionWindows();
+    decorate();
+
+    // set the frame rect to the shaded size
+    const WindowStyle &style = _screen->resource().windowStyle();
+    frame.rect.setHeight(style.title_height);
+    XResizeWindow(blackbox->XDisplay(), frame.window,
+                  frame.rect.width(), frame.rect.height());
+    return;
+  }
+
+  unsigned int button = 0u;
+  if (client.ewmh.maxv) {
+    button = (client.ewmh.maxh) ? 1u : 2u;
+  } else if (client.ewmh.maxh) {
+    button = (client.ewmh.maxv) ? 1u : 3u;
+  }
+
+  // trick maximize() into working
+  client.ewmh.maxh = client.ewmh.maxv = false;
+  const bt::Rect tmp = client.premax;
+  maximize(button);
+  client.premax = tmp;
+}
+
+
+void BlackboxWindow::setShaded(bool shaded) {
+  assert(hasWindowFunction(WindowFunctionShade));
+
+  if (client.ewmh.shaded == shaded)
+    return;
+
+  client.ewmh.shaded = shaded;
+  if (!isShaded()) {
+    if (isMaximized()) {
+      remaximize();
+    } else {
+      // set the frame rect to the normal size
+      frame.rect.setHeight(client.rect.height() + frame.margin.top +
+                           frame.margin.bottom);
+
+      XResizeWindow(blackbox->XDisplay(), frame.window,
+                    frame.rect.width(), frame.rect.height());
+    }
+
+    setState(NormalState);
+  } else {
+    // set the frame rect to the shaded size
+    const WindowStyle &style = _screen->resource().windowStyle();
+    frame.rect.setHeight(style.title_height);
+
+    XResizeWindow(blackbox->XDisplay(), frame.window,
+                  frame.rect.width(), frame.rect.height());
+
+    setState(IconicState);
+  }
+}
+
+
+void BlackboxWindow::setFullScreen(bool b) {
+  assert(hasWindowFunction(WindowFunctionFullScreen));
+
+  if (client.ewmh.fullscreen == b)
+    return;
+
+  // any fullscreen operation always unshades
+  client.ewmh.shaded = false;
+  frame.rect.setHeight(client.rect.height() + frame.margin.top
+                       + frame.margin.bottom);
+
+  bool refocus = isFocused();
+  client.ewmh.fullscreen = b;
+  if (isFullScreen()) {
+    // go go gadget-fullscreen!
+    if (!isMaximized())
+      client.premax = ::restoreGravity(frame.rect,
+                                       frame.margin,
+                                       client.wmnormal.win_gravity);
+
+    // modify decorations, functions and frame margin
+    client.decorations = NoWindowDecorations;
+    client.functions &= ~(WindowFunctionMove |
+                          WindowFunctionResize |
+                          WindowFunctionShade);
+    const WindowStyle &style = _screen->resource().windowStyle();
+    frame.margin = ::update_margin(client.decorations, style);
+
+    /*
+     * Note: we don't call ::constrain() here, because many
+     * applications are broken in this respect.  Some specify a
+     * max-size or aspect-ratio that simply doesn't cover the entire
+     * screen.  Let's try to be smarter than such applications and
+     * simply cover the entire screen.
+     */
+
+    // trick configure() into working
+    frame.rect = bt::Rect();
+    configure(_screen->screenInfo().rect());
+
+    if (isVisible())
+      changeLayer(StackingList::LayerFullScreen);
+
+    updateEWMHState();
+    updateEWMHAllowedActions();
+  } else {
+    // restore from fullscreen
+    ::update_decorations(client.decorations,
+                         client.functions,
+                         isTransient(),
+                         client.ewmh,
+                         client.motif,
+                         client.wmnormal,
+                         client.wmprotocols);
+    const WindowStyle &style = _screen->resource().windowStyle();
+    frame.margin = ::update_margin(client.decorations, style);
+
+    if (isVisible())
+      changeLayer(StackingList::LayerNormal);
+
+    if (isMaximized()) {
+      remaximize();
+    } else {
+      bt::Rect r = ::applyGravity(client.premax,
+                                  frame.margin,
+                                  client.wmnormal.win_gravity);
+      r = ::constrain(r, frame.margin, client.wmnormal, TopLeft);
+
+      // trick configure into working
+      frame.rect = bt::Rect();
+      configure(r);
+
+      updateEWMHState();
+      updateEWMHAllowedActions();
+    }
+  }
+
+  ungrabButtons();
+  grabButtons();
+
+  if (refocus)
+    (void) setInputFocus();
+}
+
+
+void BlackboxWindow::redrawWindowFrame(void) const {
+  if (client.decorations & WindowDecorationTitlebar) {
+    redrawTitle();
+    redrawLabel();
+    redrawAllButtons();
+  }
+
+  if (client.decorations & WindowDecorationBorder) {
+    const WindowStyle &style = _screen->resource().windowStyle();
+    const bt::Color &c = (isFocused()
+                          ? style.focus.frame_border
+                          : style.unfocus.frame_border);
+    XSetWindowBorder(blackbox->XDisplay(), frame.plate,
+                     c.pixel(_screen->screenNumber()));
+  }
+
+  if (client.decorations & WindowDecorationHandle) {
+    redrawHandle();
+
+    if (client.decorations & WindowDecorationGrip)
+      redrawGrips();
+  }
+}
+
+
+void BlackboxWindow::setFocused(bool focused) {
+  if (focused == client.state.focused)
+    return;
+
+  client.state.focused = isVisible() ? focused : false;
+
+  if (isVisible()) {
+    redrawWindowFrame();
+
+    if (client.state.focused) {
+      XInstallColormap(blackbox->XDisplay(), client.colormap);
+    } else {
+      if (client.ewmh.fullscreen && layer() != StackingList::LayerBelow)
+        changeLayer(StackingList::LayerBelow);
+    }
+  }
+}
+
+
+void BlackboxWindow::setState(unsigned long new_state) {
+  client.current_state = new_state;
+
+  unsigned long state[2];
+  state[0] = client.current_state;
+  state[1] = None;
+  XChangeProperty(blackbox->XDisplay(), client.window,
+                  blackbox->wmStateAtom(), blackbox->wmStateAtom(), 32,
+                  PropModeReplace, (unsigned char *) state, 2);
+
+  updateEWMHState();
+  updateEWMHAllowedActions();
+}
+
+
+void BlackboxWindow::updateEWMHState() {
+  const bt::EWMH& ewmh = blackbox->ewmh();
+
+  // set _NET_WM_STATE
+  bt::EWMH::AtomList atoms;
+  if (isModal())
+    atoms.push_back(ewmh.wmStateModal());
+  if (isShaded())
+    atoms.push_back(ewmh.wmStateShaded());
+  if (isIconic())
+    atoms.push_back(ewmh.wmStateHidden());
+  if (isFullScreen())
+    atoms.push_back(ewmh.wmStateFullscreen());
+  if (client.ewmh.maxh)
+    atoms.push_back(ewmh.wmStateMaximizedHorz());
+  if (client.ewmh.maxv)
+    atoms.push_back(ewmh.wmStateMaximizedVert());
+  if (client.ewmh.skip_taskbar)
+    atoms.push_back(ewmh.wmStateSkipTaskbar());
+  if (client.ewmh.skip_pager)
+    atoms.push_back(ewmh.wmStateSkipPager());
+
+  switch (layer()) {
+  case StackingList::LayerAbove:
+    atoms.push_back(ewmh.wmStateAbove());
+    break;
+  case StackingList::LayerBelow:
+    atoms.push_back(ewmh.wmStateBelow());
+    break;
+  default:
+    break;
+  }
+
+  if (atoms.empty())
+    ewmh.removeProperty(client.window, ewmh.wmState());
+  else
+    ewmh.setWMState(client.window, atoms);
+}
+
+
+void BlackboxWindow::updateEWMHAllowedActions() {
+  const bt::EWMH& ewmh = blackbox->ewmh();
+
+  // set _NET_WM_ALLOWED_ACTIONS
+  bt::EWMH::AtomList atoms;
+  if (! client.state.iconic) {
+    if (hasWindowFunction(WindowFunctionChangeWorkspace))
+      atoms.push_back(ewmh.wmActionChangeDesktop());
+
+    if (hasWindowFunction(WindowFunctionIconify))
+      atoms.push_back(ewmh.wmActionMinimize());
+
+    if (hasWindowFunction(WindowFunctionShade))
+      atoms.push_back(ewmh.wmActionShade());
+
+    if (hasWindowFunction(WindowFunctionMove))
+      atoms.push_back(ewmh.wmActionMove());
+
+    if (hasWindowFunction(WindowFunctionResize))
+      atoms.push_back(ewmh.wmActionResize());
+
+    if (hasWindowFunction(WindowFunctionMaximize)) {
+      atoms.push_back(ewmh.wmActionMaximizeHorz());
+      atoms.push_back(ewmh.wmActionMaximizeVert());
+    }
+
+    atoms.push_back(ewmh.wmActionFullscreen());
+  }
+
+  if (hasWindowFunction(WindowFunctionClose))
+    atoms.push_back(ewmh.wmActionClose());
+
+  if (atoms.empty())
+    ewmh.removeProperty(client.window, ewmh.wmAllowedActions());
+  else
+    ewmh.setWMAllowedActions(client.window, atoms);
+}
+
+
+void BlackboxWindow::redrawTitle(void) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const bt::Rect u(0, 0, frame.rect.width(), style.title_height);
+  bt::drawTexture(_screen->screenNumber(),
+                  (client.state.focused
+                   ? style.focus.title
+                   : style.unfocus.title),
+                  frame.title, u, u,
+                  (client.state.focused
+                   ? frame.ftitle
+                   : frame.utitle));
+}
+
+
+void BlackboxWindow::redrawLabel(void) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  bt::Rect u(0, 0, frame.label_w, style.label_height);
+  Pixmap p = (client.state.focused ? frame.flabel : frame.ulabel);
+  if (p == ParentRelative) {
+    const bt::Texture &texture =
+      (isFocused() ? style.focus.title : style.unfocus.title);
+    int offset = texture.borderWidth();
+    if (client.decorations & WindowDecorationIconify)
+      offset += style.button_width + style.title_margin;
+
+    const bt::Rect t(-(style.title_margin + offset),
+                     -(style.title_margin + texture.borderWidth()),
+                     frame.rect.width(), style.title_height);
+    bt::drawTexture(_screen->screenNumber(), texture, frame.label, t, u,
+                    (client.state.focused ? frame.ftitle : frame.utitle));
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    (client.state.focused
+                     ? style.focus.label
+                     : style.unfocus.label),
+                    frame.label, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(),
+                    ((client.state.focused)
+                     ? style.focus.text
+                     : style.unfocus.text));
+  u.setCoords(u.left()  + style.label_margin,
+              u.top() + style.label_margin,
+              u.right() - style.label_margin,
+              u.bottom() - style.label_margin);
+  bt::drawText(style.font, pen, frame.label, u,
+               style.alignment, client.visible_title);
+}
+
+
+void BlackboxWindow::redrawAllButtons(void) const {
+  if (frame.iconify_button) redrawIconifyButton();
+  if (frame.maximize_button) redrawMaximizeButton();
+  if (frame.close_button) redrawCloseButton();
+}
+
+
+void BlackboxWindow::redrawIconifyButton(bool pressed) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const bt::Rect u(0, 0, style.button_width, style.button_width);
+  Pixmap p = (pressed ? frame.pbutton :
+              (client.state.focused ? frame.fbutton : frame.ubutton));
+  if (p == ParentRelative) {
+    const bt::Texture &texture =
+      (isFocused() ? style.focus.title : style.unfocus.title);
+    const bt::Rect t(-(style.title_margin + texture.borderWidth()),
+                     -(style.title_margin + texture.borderWidth()),
+                     frame.rect.width(), style.title_height);
+    bt::drawTexture(_screen->screenNumber(), texture, frame.iconify_button,
+                    t, u, (client.state.focused
+                           ? frame.ftitle
+                           : frame.utitle));
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    (pressed ? style.pressed :
+                     (client.state.focused ? style.focus.button :
+                      style.unfocus.button)),
+                    frame.iconify_button, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(),
+                    (client.state.focused
+                     ? style.focus.foreground
+                     : style.unfocus.foreground));
+  bt::drawBitmap(style.iconify, pen, frame.iconify_button, u);
+}
+
+
+void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const bt::Rect u(0, 0, style.button_width, style.button_width);
+  Pixmap p = (pressed ? frame.pbutton :
+              (client.state.focused ? frame.fbutton : frame.ubutton));
+  if (p == ParentRelative) {
+    const bt::Texture &texture =
+      (isFocused() ? style.focus.title : style.unfocus.title);
+    int button_w = style.button_width
+                   + style.title_margin + texture.borderWidth();
+    if (client.decorations & WindowDecorationClose)
+      button_w *= 2;
+    const bt::Rect t(-(frame.rect.width() - button_w),
+                     -(style.title_margin + texture.borderWidth()),
+                     frame.rect.width(), style.title_height);
+    bt::drawTexture(_screen->screenNumber(), texture, frame.maximize_button,
+                    t, u, (client.state.focused
+                           ? frame.ftitle
+                           : frame.utitle));
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    (pressed ? style.pressed :
+                     (client.state.focused ? style.focus.button :
+                      style.unfocus.button)),
+                    frame.maximize_button, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(),
+                    (client.state.focused
+                     ? style.focus.foreground
+                     : style.unfocus.foreground));
+  bt::drawBitmap(isMaximized() ? style.restore : style.maximize,
+                 pen, frame.maximize_button, u);
+}
+
+
+void BlackboxWindow::redrawCloseButton(bool pressed) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const bt::Rect u(0, 0, style.button_width, style.button_width);
+  Pixmap p = (pressed ? frame.pbutton :
+              (client.state.focused ? frame.fbutton : frame.ubutton));
+  if (p == ParentRelative) {
+    const bt::Texture &texture =
+      (isFocused() ? style.focus.title : style.unfocus.title);
+    const int button_w = style.button_width +
+                         style.title_margin +
+                         texture.borderWidth();
+    const bt::Rect t(-(frame.rect.width() - button_w),
+                     -(style.title_margin + texture.borderWidth()),
+                     frame.rect.width(), style.title_height);
+    bt::drawTexture(_screen->screenNumber(),texture, frame.close_button, t, u,
+                    (client.state.focused ? frame.ftitle : frame.utitle));
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    (pressed ? style.pressed :
+                     (client.state.focused ? style.focus.button :
+                      style.unfocus.button)),
+                    frame.close_button, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(),
+                    (client.state.focused
+                     ? style.focus.foreground
+                     : style.unfocus.foreground));
+  bt::drawBitmap(style.close, pen, frame.close_button, u);
+}
+
+
+void BlackboxWindow::redrawHandle(void) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const bt::Rect u(0, 0, frame.rect.width(), style.handle_height);
+  bt::drawTexture(_screen->screenNumber(),
+                  (client.state.focused ? style.focus.handle :
+                                          style.unfocus.handle),
+                  frame.handle, u, u,
+                  (client.state.focused ? frame.fhandle : frame.uhandle));
+}
+
+
+void BlackboxWindow::redrawGrips(void) const {
+  const WindowStyle &style = _screen->resource().windowStyle();
+  const bt::Rect u(0, 0, style.grip_width, style.handle_height);
+  Pixmap p = (client.state.focused ? frame.fgrip : frame.ugrip);
+  if (p == ParentRelative) {
+    bt::Rect t(0, 0, frame.rect.width(), style.handle_height);
+    bt::drawTexture(_screen->screenNumber(),
+                    (client.state.focused ? style.focus.handle :
+                                            style.unfocus.handle),
+                    frame.right_grip, t, u, p);
+
+    t.setPos(-(frame.rect.width() - style.grip_width), 0);
+    bt::drawTexture(_screen->screenNumber(),
+                    (client.state.focused ? style.focus.handle :
+                                            style.unfocus.handle),
+                    frame.right_grip, t, u, p);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    (client.state.focused ? style.focus.grip :
+                                            style.unfocus.grip),
+                    frame.left_grip, u, u, p);
+
+    bt::drawTexture(_screen->screenNumber(),
+                    (client.state.focused ? style.focus.grip :
+                                            style.unfocus.grip),
+                    frame.right_grip, u, u, p);
+  }
+}
+
+
+void
+BlackboxWindow::clientMessageEvent(const XClientMessageEvent * const event) {
+  if (event->format != 32)
+    return;
+
+  const bt::EWMH& ewmh = blackbox->ewmh();
+
+  if (event->message_type == blackbox->wmChangeStateAtom()) {
+    if (event->data.l[0] == IconicState) {
+      if (hasWindowFunction(WindowFunctionIconify))
+        iconify();
+    } else if (event->data.l[0] == NormalState) {
+      activate();
+    }
+  } else if (event->message_type == ewmh.activeWindow()) {
+    activate();
+  } else if (event->message_type == ewmh.closeWindow()) {
+    if (hasWindowFunction(WindowFunctionClose))
+      close();
+  } else if (event->message_type == ewmh.moveresizeWindow()) {
+    XConfigureRequestEvent request;
+    request.window = event->window;
+    request.value_mask =
+      (event->data.l[0] >> 8) & (CWX | CWY | CWWidth | CWHeight);
+    request.x = event->data.l[1];
+    request.y = event->data.l[2];
+    request.width = event->data.l[3];
+    request.height = event->data.l[4];
+
+    const int gravity = (event->data.l[0] & 0xff);
+    const int old_gravity = client.wmnormal.win_gravity;
+    if (event->data.l[0] != 0)
+      client.wmnormal.win_gravity = gravity;
+
+    configureRequestEvent(&request);
+
+    client.wmnormal.win_gravity = old_gravity;
+  } else if (event->message_type == ewmh.wmDesktop()) {
+    if (hasWindowFunction(WindowFunctionChangeWorkspace)) {
+      const unsigned int new_workspace = event->data.l[0];
+      changeWorkspace(new_workspace);
+    }
+  } else if (event->message_type == ewmh.wmState()) {
+    Atom action = event->data.l[0],
+          first = event->data.l[1],
+         second = event->data.l[2];
+
+    if (first == ewmh.wmStateModal() || second == ewmh.wmStateModal()) {
+      if ((action == ewmh.wmStateAdd() ||
+           (action == ewmh.wmStateToggle() && ! client.ewmh.modal)) &&
+          isTransient())
+        client.ewmh.modal = true;
+      else
+        client.ewmh.modal = false;
+    }
+
+    if (hasWindowFunction(WindowFunctionMaximize)) {
+      int max_horz = 0, max_vert = 0;
+
+      if (first == ewmh.wmStateMaximizedHorz() ||
+          second == ewmh.wmStateMaximizedHorz()) {
+        max_horz = ((action == ewmh.wmStateAdd()
+                     || (action == ewmh.wmStateToggle()
+                         && !client.ewmh.maxh))
+                    ? 1 : -1);
+      }
+
+      if (first == ewmh.wmStateMaximizedVert() ||
+          second == ewmh.wmStateMaximizedVert()) {
+        max_vert = ((action == ewmh.wmStateAdd()
+                     || (action == ewmh.wmStateToggle()
+                         && !client.ewmh.maxv))
+                    ? 1 : -1);
+      }
+
+      if (max_horz != 0 || max_vert != 0) {
+        if (isMaximized())
+          maximize(0);
+        unsigned int button = 0u;
+        if (max_horz == 1 && max_vert != 1)
+          button = 3u;
+        else if (max_vert == 1 && max_horz != 1)
+          button = 2u;
+        else if (max_vert == 1 && max_horz == 1)
+          button = 1u;
+        if (button)
+          maximize(button);
+      }
+    }
+
+    if (hasWindowFunction(WindowFunctionShade)) {
+      if (first == ewmh.wmStateShaded() ||
+          second == ewmh.wmStateShaded()) {
+        if (action == ewmh.wmStateRemove())
+          setShaded(false);
+        else if (action == ewmh.wmStateAdd())
+          setShaded(true);
+        else if (action == ewmh.wmStateToggle())
+          setShaded(!isShaded());
+      }
+    }
+
+    if (first == ewmh.wmStateSkipTaskbar()
+        || second == ewmh.wmStateSkipTaskbar()
+        || first == ewmh.wmStateSkipPager()
+        || second == ewmh.wmStateSkipPager()) {
+      if (first == ewmh.wmStateSkipTaskbar()
+          || second == ewmh.wmStateSkipTaskbar()) {
+        client.ewmh.skip_taskbar = (action == ewmh.wmStateAdd()
+                                    || (action == ewmh.wmStateToggle()
+                                        && !client.ewmh.skip_taskbar));
+      }
+      if (first == ewmh.wmStateSkipPager()
+          || second == ewmh.wmStateSkipPager()) {
+        client.ewmh.skip_pager = (action == ewmh.wmStateAdd()
+                                  || (action == ewmh.wmStateToggle()
+                                      && !client.ewmh.skip_pager));
+      }
+      // we do nothing with skip_*, but others might... we should at
+      // least make sure these are present in _NET_WM_STATE
+      updateEWMHState();
+    }
+
+    if (first == ewmh.wmStateHidden() ||
+        second == ewmh.wmStateHidden()) {
+      /*
+        ignore _NET_WM_STATE_HIDDEN, the wm sets this state, not the
+        application
+      */
+    }
+
+    if (hasWindowFunction(WindowFunctionFullScreen)) {
+      if (first == ewmh.wmStateFullscreen() ||
+          second == ewmh.wmStateFullscreen()) {
+        if (action == ewmh.wmStateAdd() ||
+            (action == ewmh.wmStateToggle() &&
+             ! client.ewmh.fullscreen)) {
+          setFullScreen(true);
+        } else if (action == ewmh.wmStateToggle() ||
+                   action == ewmh.wmStateRemove()) {
+          setFullScreen(false);
+        }
+      }
+    }
+
+    if (hasWindowFunction(WindowFunctionChangeLayer)) {
+      if (first == ewmh.wmStateAbove() ||
+          second == ewmh.wmStateAbove()) {
+        if (action == ewmh.wmStateAdd() ||
+            (action == ewmh.wmStateToggle() &&
+             layer() != StackingList::LayerAbove)) {
+          changeLayer(StackingList::LayerAbove);
+        } else if (action == ewmh.wmStateToggle() ||
+                   action == ewmh.wmStateRemove()) {
+          changeLayer(StackingList::LayerNormal);
+        }
+      }
+
+      if (first == ewmh.wmStateBelow() ||
+          second == ewmh.wmStateBelow()) {
+        if (action == ewmh.wmStateAdd() ||
+            (action == ewmh.wmStateToggle() &&
+             layer() != StackingList::LayerBelow)) {
+          changeLayer(StackingList::LayerBelow);
+        } else if (action == ewmh.wmStateToggle() ||
+                   action == ewmh.wmStateRemove()) {
+          changeLayer(StackingList::LayerNormal);
+        }
+      }
+    }
+  }
+}
+
+
+void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent * const event) {
+  if (event->window != client.window)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  _screen->releaseWindow(this);
+}
+
+
+void
+BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent * const event) {
+  if (event->window != client.window)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif // DEBUG
+
+  _screen->releaseWindow(this);
+}
+
+
+void BlackboxWindow::reparentNotifyEvent(const XReparentEvent * const event) {
+  if (event->window != client.window || event->parent == frame.plate)
+    return;
+
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
+          "0x%lx.\n", client.window, event->parent);
+#endif // DEBUG
+
+  /*
+    put the ReparentNotify event back into the queue so that
+    BlackboxWindow::restore(void) can do the right thing
+  */
+  XEvent replay;
+  replay.xreparent = *event;
+  XPutBackEvent(blackbox->XDisplay(), &replay);
+
+  _screen->releaseWindow(this);
+}
+
+
+void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent * const event) {
+#ifdef    DEBUG
+  fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
+          client.window);
+#endif
+
+  switch(event->atom) {
+  case XA_WM_TRANSIENT_FOR: {
+    if (isTransient()) {
+      // remove ourselves from our transient_for
+      BlackboxWindow *win = findTransientFor();
+      if (win) {
+        win->removeTransient(this);
+      } else if (isGroupTransient()) {
+        BWindowGroup *group = findWindowGroup();
+        if (group)
+          group->removeTransient(this);
+      }
+    }
+
+    // determine if this is a transient window
+    client.transient_for = ::readTransientInfo(blackbox,
+                                               client.window,
+                                               _screen->screenInfo(),
+                                               client.wmhints);
+
+    if (isTransient()) {
+      BlackboxWindow *win = findTransientFor();
+      if (win) {
+        // add ourselves to our new transient_for
+        win->addTransient(this);
+        changeWorkspace(win->workspace());
+        changeLayer(win->layer());
+      } else if (isGroupTransient()) {
+        BWindowGroup *group = findWindowGroup();
+        if (group)
+          group->addTransient(this);
+      } else {
+        // broken client
+        client.transient_for = 0;
+      }
+    }
+
+    ::update_decorations(client.decorations,
+                         client.functions,
+                         isTransient(),
+                         client.ewmh,
+                         client.motif,
+                         client.wmnormal,
+                         client.wmprotocols);
+
+    reconfigure();
+    break;
+  }
+
+  case XA_WM_HINTS: {
+    // remove from current window group
+    BWindowGroup *group = findWindowGroup();
+    if (group) {
+      if (isTransient() && !findTransientFor() && isGroupTransient())
+        group->removeTransient(this);
+      group->removeWindow(this);
+      group = 0;
+    }
+
+    client.wmhints = ::readWMHints(blackbox, client.window);
+
+    if (client.wmhints.window_group != None) {
+      // add to new window group
+      group = ::update_window_group(client.wmhints.window_group,
+                                    blackbox,
+                                    this);
+      if (isTransient() && !findTransientFor() && isGroupTransient()) {
+        if (group)
+          group->addTransient(this);
+      }
+    }
+    break;
+  }
+
+  case XA_WM_ICON_NAME: {
+    client.icon_title = ::readWMIconName(blackbox, client.window);
+    if (client.state.iconic)
+      _screen->propagateWindowName(this);
+    break;
+  }
+
+  case XA_WM_NAME: {
+    client.title = ::readWMName(blackbox, client.window);
+
+    client.visible_title =
+      bt::ellideText(client.title, frame.label_w, bt::toUnicode("..."),
+                     _screen->screenNumber(),
+                     _screen->resource().windowStyle().font);
+    blackbox->ewmh().setWMVisibleName(client.window, client.visible_title);
+
+    if (client.decorations & WindowDecorationTitlebar)
+      redrawLabel();
+
+    _screen->propagateWindowName(this);
+    break;
+  }
+
+  case XA_WM_NORMAL_HINTS: {
+    WMNormalHints wmnormal = ::readWMNormalHints(blackbox, client.window,
+                                                 _screen->screenInfo());
+    if (wmnormal == client.wmnormal) {
+      // apps like xv and GNU emacs seem to like to repeatedly set
+      // this property over and over
+      break;
+    }
+
+    client.wmnormal = wmnormal;
+
+    ::update_decorations(client.decorations,
+                         client.functions,
+                         isTransient(),
+                         client.ewmh,
+                         client.motif,
+                         client.wmnormal,
+                         client.wmprotocols);
+
+    reconfigure();
+    break;
+  }
+
+  default: {
+    if (event->atom == blackbox->wmProtocolsAtom()) {
+      client.wmprotocols = ::readWMProtocols(blackbox, client.window);
+
+      ::update_decorations(client.decorations,
+                           client.functions,
+                           isTransient(),
+                           client.ewmh,
+                           client.motif,
+                           client.wmnormal,
+                           client.wmprotocols);
+
+      reconfigure();
+    } else if (event->atom == blackbox->motifWmHintsAtom()) {
+      client.motif = ::readMotifWMHints(blackbox, client.window);
+
+      ::update_decorations(client.decorations,
+                           client.functions,
+                           isTransient(),
+                           client.ewmh,
+                           client.motif,
+                           client.wmnormal,
+                           client.wmprotocols);
+
+      reconfigure();
+    } else if (event->atom == blackbox->ewmh().wmStrut()) {
+      if (! client.strut) {
+        client.strut = new bt::EWMH::Strut;
+        _screen->addStrut(client.strut);
+      }
+
+      blackbox->ewmh().readWMStrut(client.window, client.strut);
+      if (client.strut->left || client.strut->right ||
+          client.strut->top || client.strut->bottom) {
+        _screen->updateStrut();
+      } else {
+        _screen->removeStrut(client.strut);
+        delete client.strut;
+      }
+    }
+
+    break;
+  }
+  } // switch
+}
+
+
+void BlackboxWindow::exposeEvent(const XExposeEvent * const event) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
+#endif
+
+  if (frame.title == event->window)
+    redrawTitle();
+  else if (frame.label == event->window)
+    redrawLabel();
+  else if (frame.close_button == event->window)
+    redrawCloseButton();
+  else if (frame.maximize_button == event->window)
+    redrawMaximizeButton();
+  else if (frame.iconify_button == event->window)
+    redrawIconifyButton();
+  else if (frame.handle == event->window)
+    redrawHandle();
+  else if (frame.left_grip == event->window ||
+           frame.right_grip == event->window)
+    redrawGrips();
+}
+
+
+void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *
+                                           const event) {
+  if (event->window != client.window || client.state.iconic)
+    return;
+
+  if (event->value_mask & CWBorderWidth)
+    client.old_bw = event->border_width;
+
+  if (event->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
+    bt::Rect req = frame.rect;
+
+    if (event->value_mask & (CWX | CWY)) {
+      req = ::restoreGravity(req, frame.margin, client.wmnormal.win_gravity);
+
+      if (event->value_mask & CWX)
+        req.setX(event->x);
+      if (event->value_mask & CWY)
+        req.setY(event->y);
+
+      req = ::applyGravity(req, frame.margin, client.wmnormal.win_gravity);
+    }
+
+    if (event->value_mask & (CWWidth | CWHeight)) {
+      if (event->value_mask & CWWidth)
+        req.setWidth(event->width + frame.margin.left + frame.margin.right);
+      if (event->value_mask & CWHeight)
+        req.setHeight(event->height + frame.margin.top + frame.margin.bottom);
+    }
+
+    configure(req);
+  }
+
+  if (event->value_mask & CWStackMode) {
+    switch (event->detail) {
+    case Below:
+    case BottomIf:
+      _screen->lowerWindow(this);
+      break;
+
+    case Above:
+    case TopIf:
+    default:
+      _screen->raiseWindow(this);
+      break;
+    }
+  }
+}
+
+
+void BlackboxWindow::buttonPressEvent(const XButtonEvent * const event) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (frame.maximize_button == event->window) {
+    if (event->button < 4)
+      redrawMaximizeButton(true);
+  } else if (frame.iconify_button == event->window) {
+    if (event->button == 1)
+      redrawIconifyButton(true);
+  } else if (frame.close_button == event->window) {
+    if (event->button == 1)
+      redrawCloseButton(true);
+  } else {
+    if (event->button == 1
+        || (event->button == 3 && event->state == Mod1Mask)) {
+      frame.grab_x = event->x_root - frame.rect.x();
+      frame.grab_y = event->y_root - frame.rect.y();
+
+      _screen->raiseWindow(this);
+
+      if (! client.state.focused)
+        (void) setInputFocus();
+      else
+        XInstallColormap(blackbox->XDisplay(), client.colormap);
+
+      if (frame.plate == event->window) {
+        XAllowEvents(blackbox->XDisplay(), ReplayPointer, event->time);
+      } else if ((frame.title == event->window
+                  || frame.label == event->window)
+                 && hasWindowFunction(WindowFunctionShade)) {
+        if ((event->time - lastButtonPressTime <=
+             blackbox->resource().doubleClickInterval()) ||
+            event->state == ControlMask) {
+          lastButtonPressTime = 0;
+          setShaded(!isShaded());
+        } else {
+          lastButtonPressTime = event->time;
+        }
+      }
+    } else if (event->button == 2) {
+      _screen->lowerWindow(this);
+    } else if (event->button == 3
+               || (event->button == 3 && event->state == Mod4Mask)) {
+      const int extra = _screen->resource().windowStyle().frame_border_width;
+      const bt::Rect rect(client.rect.x() - extra,
+                          client.rect.y() - extra,
+                          client.rect.width() + (extra * 2),
+                          client.rect.height() + (extra * 2));
+
+      Windowmenu *windowmenu = _screen->windowmenu(this);
+      windowmenu->popup(event->x_root, event->y_root, rect);
+    } else if (blackbox->resource().shadeWindowWithMouseWheel()) {
+      if (event->button == 4
+       && hasWindowFunction(WindowFunctionShade)
+       && !isShaded()) {
+        setShaded(true);
+      } else if (event->button == 5
+       && hasWindowFunction(WindowFunctionShade)
+       && isShaded()) {
+        setShaded(false);
+      }
+    }
+  }
+}
+
+
+void BlackboxWindow::buttonReleaseEvent(const XButtonEvent * const event) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  const WindowStyle &style = _screen->resource().windowStyle();
+  if (event->window == frame.maximize_button) {
+    if (event->button < 4) {
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width)) {
+        maximize(event->button);
+        _screen->raiseWindow(this);
+      } else {
+        redrawMaximizeButton();
+      }
+    }
+  } else if (event->window == frame.iconify_button) {
+    if (event->button == 1) {
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width))
+        iconify();
+      else
+        redrawIconifyButton();
+    }
+  } else if (event->window == frame.close_button) {
+    if (event->button == 1) {
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width))
+        close();
+      redrawCloseButton();
+    }
+  } else if (client.state.moving) {
+    finishMove();
+  } else if (client.state.resizing) {
+    finishResize();
+  } else if (event->window == frame.window) {
+    if (event->button == 2 && event->state == Mod1Mask)
+      XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
+  }
+}
+
+
+void BlackboxWindow::motionNotifyEvent(const XMotionEvent * const event) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (hasWindowFunction(WindowFunctionMove)
+      && !client.state.resizing
+      && event->state & Button1Mask
+      && (frame.title == event->window || frame.label == event->window
+          || frame.handle == event->window || frame.window == event->window)) {
+    if (! client.state.moving)
+      startMove();
+    else
+      continueMove(event->x_root, event->y_root);
+  } else if (hasWindowFunction(WindowFunctionResize)
+             && (event->state & Button1Mask
+                 && (event->window == frame.right_grip
+                     || event->window == frame.left_grip))
+             || (event->state & Button3Mask
+                 && event->state & Mod1Mask
+                 && event->window == frame.window)) {
+    if (!client.state.resizing)
+      startResize(event->window);
+    else
+      continueResize(event->x_root, event->y_root);
+  }
+}
+
+
+void BlackboxWindow::enterNotifyEvent(const XCrossingEvent * const event) {
+  if (event->window != frame.window || event->mode != NotifyNormal)
+    return;
+
+  if (blackbox->resource().focusModel() == ClickToFocusModel || !isVisible())
+    return;
+
+  switch (windowType()) {
+  case WindowTypeDesktop:
+  case WindowTypeDock:
+    // these types cannot be focused w/ sloppy focus
+    return;
+
+  default:
+    break;
+  }
+
+  XEvent next;
+  bool leave = False, inferior = False;
+
+  while (XCheckTypedWindowEvent(blackbox->XDisplay(), event->window,
+                                LeaveNotify, &next)) {
+    if (next.type == LeaveNotify && next.xcrossing.mode == NotifyNormal) {
+      leave = True;
+      inferior = (next.xcrossing.detail == NotifyInferior);
+    }
+  }
+
+  if ((! leave || inferior) && ! isFocused())
+    (void) setInputFocus();
+
+  if (blackbox->resource().autoRaise())
+    timer->start();
+}
+
+
+void
+BlackboxWindow::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
+  if (!(blackbox->resource().focusModel() == SloppyFocusModel
+        && blackbox->resource().autoRaise()))
+    return;
+
+  if (timer->isTiming())
+    timer->stop();
+}
+
+
+#ifdef    SHAPE
+void BlackboxWindow::shapeEvent(const XEvent * const /*unused*/)
+{ if (client.state.shaped) configureShape(); }
+#endif // SHAPE
+
+
+/*
+ *
+ */
+void BlackboxWindow::restore(void) {
+  XChangeSaveSet(blackbox->XDisplay(), client.window, SetModeDelete);
+  XSelectInput(blackbox->XDisplay(), client.window, NoEventMask);
+  XSelectInput(blackbox->XDisplay(), frame.plate, NoEventMask);
+
+  client.state.visible = false;
+
+  /*
+    remove WM_STATE unless the we are shutting down (in which case we
+    want to make sure we preserve the state across restarts).
+  */
+  if (!blackbox->shuttingDown()) {
+    clearState(blackbox, client.window);
+  } else if (isShaded() && !isIconic()) {
+    // do not leave a shaded window as an icon unless it was an icon
+    setState(NormalState);
+  }
+
+  client.rect = ::restoreGravity(frame.rect, frame.margin,
+                                 client.wmnormal.win_gravity);
+
+  blackbox->XGrabServer();
+
+  XUnmapWindow(blackbox->XDisplay(), frame.window);
+  XUnmapWindow(blackbox->XDisplay(), client.window);
+
+  XSetWindowBorderWidth(blackbox->XDisplay(), client.window, client.old_bw);
+  if (isMaximized()) {
+    // preserve the original size
+    client.rect = client.premax;
+    XMoveResizeWindow(blackbox->XDisplay(), client.window,
+                      client.premax.x(),
+                      client.premax.y(),
+                      client.premax.width(),
+                      client.premax.height());
+  } else {
+    XMoveWindow(blackbox->XDisplay(), client.window,
+                client.rect.x() - frame.rect.x(),
+                client.rect.y() - frame.rect.y());
+  }
+
+  blackbox->XUngrabServer();
+
+  XEvent unused;
+  if (!XCheckTypedWindowEvent(blackbox->XDisplay(), client.window,
+                              ReparentNotify, &unused)) {
+    /*
+      according to the ICCCM, the window manager is responsible for
+      reparenting the window back to root... however, we don't want to
+      do this if the window has been reparented by someone else
+      (i.e. not us).
+    */
+    XReparentWindow(blackbox->XDisplay(), client.window,
+                    _screen->screenInfo().rootWindow(),
+                    client.rect.x(), client.rect.y());
+  }
+
+  if (blackbox->shuttingDown())
+    XMapWindow(blackbox->XDisplay(), client.window);
+}
+
+
+// timer for autoraise
+void BlackboxWindow::timeout(bt::Timer *)
+{ _screen->raiseWindow(this); }
+
+
+void BlackboxWindow::startMove() {
+  // begin a move
+  XGrabPointer(blackbox->XDisplay(), frame.window, false,
+               Button1MotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync, None,
+               blackbox->resource().cursors().move, blackbox->XTime());
+
+  client.state.moving = true;
+
+  if (! blackbox->resource().opaqueMove()) {
+    blackbox->XGrabServer();
+
+    frame.changing = frame.rect;
+    _screen->showGeometry(BScreen::Position, frame.changing);
+
+    bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
+    const int bw = _screen->resource().windowStyle().frame_border_width,
+              hw = bw / 2;
+    pen.setGCFunction(GXxor);
+    pen.setLineWidth(bw);
+    pen.setSubWindowMode(IncludeInferiors);
+    XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
+                   pen.gc(),
+                   frame.changing.x() + hw,
+                   frame.changing.y() + hw,
+                   frame.changing.width() - bw,
+                   frame.changing.height() - bw);
+  }
+}
+
+
+static
+void collisionAdjust(int *dx, int *dy, int x, int y,
+                     unsigned int width, unsigned int height,
+                     const bt::Rect& rect, int snap_distance,
+                     bool snapCenter = false)
+{
+  // window corners
+  const int wleft = x,
+           wright = x + width - 1,
+             wtop = y,
+          wbottom = y + height - 1,
+  // left, right, top + bottom are for rect, douterleft = left border of rect
+       dinnerleft = abs(wleft - rect.left()),
+      dinnerright = abs(wright - rect.right()),
+        dinnertop = abs(wtop - rect.top()),
+     dinnerbottom = abs(wbottom - rect.bottom()),
+       douterleft = abs(wright - rect.left()),
+      douterright = abs(wleft - rect.right()),
+        doutertop = abs(wbottom - rect.top()),
+     douterbottom = abs(wtop - rect.bottom());
+
+  if ((wtop <= rect.bottom() && wbottom >= rect.top())
+      || doutertop <= snap_distance
+      || douterbottom <= snap_distance) {
+    // snap left or right
+    if (douterleft <= dinnerleft && douterleft <= snap_distance)
+      // snap outer left
+      *dx = (x - (rect.left() - width));
+    else if (douterright <= dinnerright && douterright <= snap_distance)
+      // snap outer right
+      *dx = (x - rect.right() - 1);
+    else if (dinnerleft <= dinnerright && dinnerleft < snap_distance)
+      // snap inner left
+      *dx = (x - rect.left());
+    else if (dinnerright < snap_distance)
+      // snap inner right
+      *dx = (x - (rect.right() - width + 1));
+  }
+
+  if ((wleft <= rect.right() && wright >= rect.left())
+      || douterleft <= snap_distance
+      || douterright <= snap_distance) {
+    // snap top or bottom
+    if (doutertop <= dinnertop && doutertop <= snap_distance)
+      // snap outer top
+      *dy = (y - (rect.top() - height));
+    else if (douterbottom <= dinnerbottom && douterbottom <= snap_distance)
+      // snap outer bottom
+      *dy = (y - rect.bottom() - 1);
+    else if (dinnertop <= dinnerbottom && dinnertop < snap_distance)
+      // snap inner top
+      *dy = (y - rect.top());
+    else if (dinnerbottom < snap_distance)
+      // snap inner bottom
+      *dy = (y - (rect.bottom() - height + 1));
+  }
+
+  if (snapCenter) {
+    const int cwx = x + width / 2;
+    const int cwy = y + height / 2;
+    const int crx = rect.x() + rect.width() / 2;
+    const int cry = rect.y() + rect.height() / 2;
+    const int cdx = abs(cwx - crx);
+    const int cdy = abs(cwy - cry);
+    if (cdx <= snap_distance)
+      // snap to horizontal center
+      *dx = x - (rect.x() + ((rect.width() - width) / 2));
+    if (cdy <= snap_distance)
+      // snap to vertical center
+      *dy = y - (rect.y() + ((rect.height() - height) / 2));
+  }
+}
+
+
+void BlackboxWindow::snapAdjust(int *x, int *y) {
+  int nx, ny, dx, dy, init_dx, init_dy;
+  const int edge_distance = blackbox->resource().edgeSnapThreshold();
+  const int win_distance  = blackbox->resource().windowSnapThreshold();
+
+  nx = (win_distance > edge_distance) ? win_distance : edge_distance;
+  ny = (win_distance > edge_distance) ? win_distance : edge_distance;
+  dx = init_dx = ++nx; dy = init_dy = ++ny;
+
+  if (edge_distance) {
+    collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(), frame.rect.height(),
+                    _screen->availableArea(), edge_distance, true);
+    nx = (dx != init_dx && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
+    ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
+    if (!blackbox->resource().fullMaximization()) {
+      collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(), frame.rect.height(),
+                      _screen->screenInfo().rect(), edge_distance);
+      nx = (dx != init_dx && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
+      ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
+    }
+  }
+  if (win_distance) {
+    StackingList::const_iterator it = _screen->stackingList().begin(),
+                                end = _screen->stackingList().end();
+    for (; it != end; ++it) {
+      BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
+      if (win && win != this &&
+          win->workspace() == _screen->currentWorkspace()) {
+        collisionAdjust(&dx, &dy, *x, *y, frame.rect.width(),
+                        frame.rect.height(), win->frame.rect, win_distance);
+        nx = (dx != init_dx && abs(dx) < abs(nx)) ? dx : nx; dx = init_dx;
+        ny = (dy != init_dy && abs(dy) < abs(ny)) ? dy : ny; dy = init_dy;
+      }
+    }
+  }
+
+  *x = (nx != init_dx) ? (*x - nx) : *x;
+  *y = (ny != init_dy) ? (*y - ny) : *y;
+}
+
+
+void BlackboxWindow::continueMove(int x_root, int y_root) {
+  int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
+
+  snapAdjust(&dx, &dy);
+
+  if (blackbox->resource().opaqueMove()) {
+    configure(dx, dy, frame.rect.width(), frame.rect.height());
+  } else {
+    bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
+    const int bw = _screen->resource().windowStyle().frame_border_width,
+              hw = bw / 2;
+    pen.setGCFunction(GXxor);
+    pen.setLineWidth(bw);
+    pen.setSubWindowMode(IncludeInferiors);
+    XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
+                   pen.gc(),
+                   frame.changing.x() + hw,
+                   frame.changing.y() + hw,
+                   frame.changing.width() - bw,
+                   frame.changing.height() - bw);
+
+    frame.changing.setPos(dx, dy);
+
+    XDrawRectangle(blackbox->XDisplay(), _screen->screenInfo().rootWindow(),
+                   pen.gc(),
+                   frame.changing.x() + hw,
+                   frame.changing.y() + hw,
+                   frame.changing.width() - bw,
+                   frame.changing.height() - bw);
+  }
+
+  _screen->showGeometry(BScreen::Position, bt::Rect(dx, dy, 0, 0));
+}
+
+
+void BlackboxWindow::finishMove() {
+  XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
+
+  client.state.moving = false;
+
+  if (!blackbox->resource().opaqueMove()) {
+    bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
+    const int bw = _screen->resource().windowStyle().frame_border_width,
+              hw = bw / 2;
+    pen.setGCFunction(GXxor);
+    pen.setLineWidth(bw);
+    pen.setSubWindowMode(IncludeInferiors);
+    XDrawRectangle(blackbox->XDisplay(),
+                   _screen->screenInfo().rootWindow(),
+                   pen.gc(),
+                   frame.changing.x() + hw,
+                   frame.changing.y() + hw,
+                   frame.changing.width() - bw,
+                   frame.changing.height() - bw);
+    blackbox->XUngrabServer();
+
+    configure(frame.changing);
+  } else {
+    configure(frame.rect);
+  }
+
+  _screen->hideGeometry();
+}
+
+
+void BlackboxWindow::startResize(Window window) {
+  if (frame.grab_x < (signed) frame.rect.width() / 2) {
+    if (frame.grab_y < (signed) frame.rect.height() / 2)
+      frame.corner = BottomRight;
+    else
+      frame.corner = TopRight;
+  } else {
+    if (frame.grab_y < (signed) frame.rect.height() / 2)
+      frame.corner = BottomLeft;
+    else
+      frame.corner = TopLeft;
+  }
+
+  Cursor cursor = None;
+  switch (frame.corner) {
+  case TopLeft:
+    cursor = blackbox->resource().cursors().resize_bottom_right;
+    frame.grab_x = frame.rect.width() - frame.grab_x;
+    frame.grab_y = frame.rect.height() - frame.grab_y;
+    break;
+  case BottomLeft:
+    cursor = blackbox->resource().cursors().resize_top_right;
+    frame.grab_x = frame.rect.width() - frame.grab_x;
+    break;
+  case TopRight:
+    cursor = blackbox->resource().cursors().resize_bottom_left;
+    frame.grab_y = frame.rect.height() - frame.grab_y;
+    break;
+  case BottomRight:
+    cursor = blackbox->resource().cursors().resize_top_left;
+    break;
+  }
+
+  // begin a resize
+  XGrabPointer(blackbox->XDisplay(), window, False,
+               ButtonMotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync, None, cursor, blackbox->XTime());
+
+  client.state.resizing = true;
+
+  frame.changing = constrain(frame.rect, frame.margin, client.wmnormal,
+                             Corner(frame.corner));
+
+  if (!blackbox->resource().opaqueResize()) {
+    blackbox->XGrabServer();
+
+    bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
+    const int bw = _screen->resource().windowStyle().frame_border_width,
+              hw = bw / 2;
+    pen.setGCFunction(GXxor);
+    pen.setLineWidth(bw);
+    pen.setSubWindowMode(IncludeInferiors);
+    XDrawRectangle(blackbox->XDisplay(),
+                   _screen->screenInfo().rootWindow(),
+                   pen.gc(),
+                   frame.changing.x() + hw,
+                   frame.changing.y() + hw,
+                   frame.changing.width() - bw,
+                   frame.changing.height() - bw);
+  } else {
+    // unset maximized state when resized
+    if (isMaximized())
+      maximize(0);
+  }
+
+  showGeometry(frame.changing);
+}
+
+
+void BlackboxWindow::continueResize(int x_root, int y_root) {
+  // continue a resize
+  const bt::Rect curr = frame.changing;
+
+  switch (frame.corner) {
+  case TopLeft:
+  case BottomLeft:
+    frame.changing.setCoords(frame.changing.left(),
+                             frame.changing.top(),
+                             std::max<signed>(x_root + frame.grab_x,
+                                              frame.changing.left()
+                                              + (frame.margin.left
+                                                 + frame.margin.right + 1)),
+                             frame.changing.bottom());
+    break;
+  case TopRight:
+  case BottomRight:
+    frame.changing.setCoords(std::min<signed>(x_root - frame.grab_x,
+                                              frame.changing.right()
+                                              - (frame.margin.left
+                                                 + frame.margin.right + 1)),
+                             frame.changing.top(),
+                             frame.changing.right(),
+                             frame.changing.bottom());
+    break;
+  }
+
+  switch (frame.corner) {
+  case TopLeft:
+  case TopRight:
+    frame.changing.setCoords(frame.changing.left(),
+                             frame.changing.top(),
+                             frame.changing.right(),
+                             std::max<signed>(y_root + frame.grab_y,
+                                              frame.changing.top()
+                                              + (frame.margin.top
+                                                 + frame.margin.bottom + 1)));
+    break;
+  case BottomLeft:
+  case BottomRight:
+    frame.changing.setCoords(frame.changing.left(),
+                             std::min<signed>(y_root - frame.grab_y,
+                                              frame.rect.bottom()
+                                              - (frame.margin.top
+                                                 + frame.margin.bottom + 1)),
+                             frame.changing.right(),
+                             frame.changing.bottom());
+    break;
+  }
+
+  frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
+                             Corner(frame.corner));
+
+  if (curr != frame.changing) {
+    if (blackbox->resource().opaqueResize()) {
+      configure(frame.changing);
+    } else {
+      bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
+      const int bw = _screen->resource().windowStyle().frame_border_width,
+                hw = bw / 2;
+      pen.setGCFunction(GXxor);
+      pen.setLineWidth(bw);
+      pen.setSubWindowMode(IncludeInferiors);
+      XDrawRectangle(blackbox->XDisplay(),
+                     _screen->screenInfo().rootWindow(),
+                     pen.gc(),
+                     curr.x() + hw,
+                     curr.y() + hw,
+                     curr.width() - bw,
+                     curr.height() - bw);
+
+      XDrawRectangle(blackbox->XDisplay(),
+                     _screen->screenInfo().rootWindow(),
+                     pen.gc(),
+                     frame.changing.x() + hw,
+                     frame.changing.y() + hw,
+                     frame.changing.width() - bw,
+                     frame.changing.height() - bw);
+    }
+
+    showGeometry(frame.changing);
+  }
+}
+
+
+void BlackboxWindow::finishResize() {
+
+  if (!blackbox->resource().opaqueResize()) {
+    bt::Pen pen(_screen->screenNumber(), bt::Color(0xff, 0xff, 0xff));
+    const int bw = _screen->resource().windowStyle().frame_border_width,
+              hw = bw / 2;
+    pen.setGCFunction(GXxor);
+    pen.setLineWidth(bw);
+    pen.setSubWindowMode(IncludeInferiors);
+    XDrawRectangle(blackbox->XDisplay(),
+                   _screen->screenInfo().rootWindow(),
+                   pen.gc(),
+                   frame.changing.x() + hw,
+                   frame.changing.y() + hw,
+                   frame.changing.width() - bw,
+                   frame.changing.height() - bw);
+
+    blackbox->XUngrabServer();
+
+    // unset maximized state when resized
+    if (isMaximized())
+      maximize(0);
+  }
+
+  client.state.resizing = false;
+
+  XUngrabPointer(blackbox->XDisplay(), blackbox->XTime());
+
+  _screen->hideGeometry();
+
+  frame.changing = constrain(frame.changing, frame.margin, client.wmnormal,
+                             Corner(frame.corner));
+  configure(frame.changing);
+}
+
+
+/*
+ * show the geometry of the window based on rectangle r.
+ * The logical width and height are used here.  This refers to the user's
+ * perception of the window size (for example an xterm resizes in cells,
+ * not in pixels).  No extra work is needed if there is no difference between
+ * the logical and actual dimensions.
+ */
+void BlackboxWindow::showGeometry(const bt::Rect &r) const {
+  unsigned int w = r.width(), h = r.height();
+
+  // remove the window frame
+  w -= frame.margin.left + frame.margin.right;
+  h -= frame.margin.top + frame.margin.bottom;
+
+  if (client.wmnormal.flags & PResizeInc) {
+    if (client.wmnormal.flags & (PMinSize|PBaseSize)) {
+      w -= ((client.wmnormal.base_width)
+            ? client.wmnormal.base_width
+            : client.wmnormal.min_width);
+      h -= ((client.wmnormal.base_height)
+            ? client.wmnormal.base_height
+            : client.wmnormal.min_height);
+    }
+
+    w /= client.wmnormal.width_inc;
+    h /= client.wmnormal.height_inc;
+  }
+
+  _screen->showGeometry(BScreen::Size, bt::Rect(0, 0, w, h));
+}
+
+
+// see my rant above for an explanation of this operator
+bool operator==(const WMNormalHints &x, const WMNormalHints &y) {
+  return (x.flags == y.flags
+          && x.min_width == y.min_width
+          && x.min_height == y.min_height
+          && x.max_width == y.max_width
+          && x.max_height == y.max_height
+          && x.width_inc == y.width_inc
+          && x.height_inc == y.height_inc
+          && x.min_aspect_x == y.min_aspect_x
+          && x.min_aspect_y == y.min_aspect_y
+          && x.max_aspect_x == y.max_aspect_x
+          && x.max_aspect_y == y.max_aspect_y
+          && x.base_width == y.base_width
+          && x.base_height == y.base_height
+          && x.win_gravity == y.win_gravity);
+}
diff --git a/.pc/110_fix-spelling.patch/doc/blackbox.1.in b/.pc/110_fix-spelling.patch/doc/blackbox.1.in
new file mode 100644
index 0000000..2d57c8f
--- /dev/null
+++ b/.pc/110_fix-spelling.patch/doc/blackbox.1.in
@@ -0,0 +1,1151 @@
+.\"
+.\" nroff source for blackbox.1 man page
+.\"
+.\" Copyright 2002 by R.B. "Brig" Young II <secretsaregood@yahoo.com>
+.\" Written using gvim, available at http://www.vim.org/
+.\"
+.\" See the file COPYING in the source directory 
+.\" root for License specifics and restrictions
+.\" 
+.\" Updated for Blackbox 0.65.0 release on 18 Sep 2002.
+.\"
+.\"
+.\" Indented preformat macro. 
+.de EX
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.\"
+.\" * * * * * Section * * * * *
+.\"
+.\" ***** SubSection *****
+.\"
+.TH blackbox 1 "September 18, 2002" "0.65.0"
+.\"
+.\" * * * * * NAME * * * * * 
+.\"
+.SH NAME
+blackbox \- a window manager for X11
+.\"
+.\" * * * * * SYNOPSIS * * * * * 
+.\"
+.SH SYNOPSIS
+.BR "blackbox" " \-help | \-version"
+.br
+.B blackbox 
+.RI "[ \-rc" " rcfile " "] [ \-display" " display " ]
+.\"
+.\" * * * * * DESCRIPTION * * * * * 
+.\"
+.SH DESCRIPTION
+
+.\" ----- overview -----
+Blackbox is a window manager for the Open Group's
+X Window System, Version 11 Release 6 and above.
+Its design is meant to be visually minimalist and fast.
+.PP
+.\" ----- usage overview -----
+Blackbox is similar to the NeXT interface and
+Windowmaker. Applications are launched using a
+menu which is accessed by right clicking on the
+root window. Workspaces, a system of virtual 
+desktops are controlled via a menu which is accessed 
+by middle clicking on the root window and by using 
+the toolbar. Individual windows can be controlled by 
+buttons on the title bar and more options are available 
+by right clicking on the title bar.
+.PP
+.\" ----- design overview -----
+Blackbox is able to generate beautiful window
+decorations on the fly at high speed. Themes,
+called styles in Blackbox terminology, are very
+flexible but the use of pixmaps has been 
+purposefully avoided to eliminate dependencies 
+and excess memory usage.
+.PP
+.\" ----- bbtools overview -----
+Blackbox itself does not directly handle key
+bindings like most other window managers. This
+task is handled by a separate utility called
+bbkeys. Although Blackbox has a built-in
+workspace (paging) system, bbpager, which provides
+a graphical pager, is popular with many users.
+bbkeys, bbpager and several other bbtools can be found
+by going to
+.EX 0
+.B http://bbtools.thelinuxcommunity.org/
+.EE
+.\" ----- slit overview -----
+The slit is an edge of the screen which can
+hold specially designed programs called dock
+apps (from Windowmaker). In addition, the 
+popular program gkrellm will also run in the slit. 
+There is a huge selection of dockapps available 
+and they run the gamut from must-have gadgets 
+to utterly useless (but cute and/or funny) eye candy. 
+.EX 0
+.B http://www.bensinclair.com/dockapp/
+.B http://dockapps.org/
+.EE
+.\"
+.\" * * * * * OPTIONS * * * * * 
+.\"
+.SH OPTIONS 
+Blackbox supports the following command line options: 
+.TP
+.\" ----- help -----
+.B \-help 
+Display command line options, compiled-in features, and exit.
+.TP
+.\" ----- version -----
+.B \-version
+Display version and exit.
+.TP
+.\" ----- rcfile -----
+.BI \-rc \ rcfile
+Use an alternate resource file.
+.TP 
+.\" ----- display -----
+.BI \-display \ display
+Start Blackbox on the specified display, and set the 
+.B DISPLAY 
+environment variable to this value for programs 
+started by Blackbox.
+.PP
+.\"
+.\" * * * * * STARTING AND EXITING BLACKBOX * * * * *
+.\"
+.SH STARTING AND EXITING BLACKBOX
+The most common method for starting Blackbox 
+is to place the the command "blackbox" (no quotes)
+at the end of your 
+.B ~/.xinitrc
+or
+.B ~/.xsession
+file. 
+The advantage of putting Blackbox at the end of the file
+is that the X Server will shutdown when you exit 
+Blackbox. Blackbox can also be started from 
+the command line of a terminal program like xterm in an
+X session that does not already have a window manager running.
+.PP
+On startup, Blackbox will look for 
+.B ~/.blackboxrc 
+and use the resource 
+.B session.menuFile 
+to determine where to get the menu for the session. 
+If this file is not found Blackbox will use
+.B @defaultmenu@
+as the menu file. If that fails as well Blackbox 
+will use a default menu that contains commands 
+to start an xterm as well as restart and exit the window manager.
+The other resources available in the 
+.B ~/.blackboxrc 
+file are discussed later in this manual under 
+the heading RESOURCE FILE.
+
+On exit, Blackbox writes its current configuration to 
+.B ~/.blackboxrc. 
+.EX 0
+.B NOTE: 
+If ~/.blackboxrc is modified during a Blackbox 
+session, Blackbox must be restarted with the 
+"restart" command on the main menu or the changes 
+will be lost on exit. Restart causes Blackbox to 
+re-read ~/.blackboxrc and apply the changes immediately.
+.EE
+Blackbox can be exited by selecting "exit" on
+the main menu (discussed shortly), killing it
+gently from a terminal or by the X Window System 
+shutdown hot key combo Ctrl+Alt+BackSpace. 
+.PP
+.\"
+.\" * * * * * USING BLACKBOX * * * * * 
+.\"
+.SH USING BLACKBOX
+.PP
+A three button mouse has the following functions 
+when clicking on the root window:
+.TP
+.BR "Button Two" "  (Middle Button)"
+Open workspace menu
+.TP
+.BR "Button Three" "  (Right Button)"
+Open main menu
+.TP
+Note that Button One (Left Button) is not used.
+.\"
+.\" ***** MAIN MENU ******
+.\"
+.TP
+.B Main Menu
+The default installation assumes you have a number 
+of common X Window System programs in their typical
+locations. The default menu is defined by a plain text
+file named 'menu'. It is heavily commented and covers a
+number of details of menu file syntax. This file can also
+be edited graphically by using the extension program bbconf
+which makes menu creation very easy. Menu file syntax is
+discussed later in this manual.
+.EX 0
+.\" ----- main menu caveat -----
+.B Caveat:
+Menus can run arbitrary command lines, but
+if you wish to use a complex command line 
+it is best to place it in a shell script. 
+Remember to put #!/bin/sh on the first 
+line and chmod 755 on the file to make it 
+executable.
+.EE
+.\"
+.\" ***** WORKSPACE MENU *****
+.\"
+.TP
+.B Workspace Menu
+This menu gives the user control of the workspace 
+system. The user can create a new workspace,
+remove the last workspace or go to an application
+via either the icon menu or a workspace entry.
+Workspaces are listed by name. Clicking on the 
+workspace name will take you to that workspace
+with focus on the program under the mouse. If 
+there are programs already running in the 
+workspace, they will appear in a pop-out menu. 
+Clicking on the application name will jump to 
+the workspace and focus that application. If a
+middle click is used the window will be brought to
+the current workspace.
+
+Blackbox uses an external program, bbpager,
+to provide a traditional, graphical paging
+interface to the workspace system. Many Blackbox
+users run another extension program - bbkeys -
+to provide keyboard shortcuts for workspace control.
+.EX 0
+.\" ----- workspace caveat -----
+.B Caveat: 
+To name a workspace the user must right
+click on the toolbar, select "Edit current 
+workspace name," type the workspace name, 
+And_Press_Enter to finish.
+.EE
+Workspaces can also be named in the .blackboxrc
+file as described in RESOURCES.
+.\"
+.\" ***** THE SLIT *****
+.\"
+.TP
+.B The Slit
+The Slit provides a user positionable window for 
+running utility programs called "dockapps". To 
+learn more about dockapps refer to the web sites
+mentioned in the Description. Dockapps 
+automatically run in the slit in most cases, but
+may require a special command switch.  
+Often, \-w is used for "withdrawn" into the slit.
+
+gkrellm is a very useful and modern dockapp that
+gives the user near real time information on 
+machine performance. Other dockapps include clocks,
+notepads, pagers, key grabbers, fishbowls, fire
+places and many, many others. 
+
+Only mouse button three is captured by the 
+Blackbox slit. This menu allows the user to change 
+the position of the slit, and sets the state of 
+Always on top, and Auto hide. These all do what 
+the user expects.
+
+.EX 3
+.\" ----- slit caveat -----
+.B Caveat:
+When starting Dockapps from an external script
+a race condition can take place where the shell
+rapidly forks all of the dockapps, which then
+take varied and random times to draw themselves 
+for the first time. To get the dockapps to start 
+in a given order, follow each dockapp with 
+sleep 2; This ensures that each dockapp is placed 
+in the correct order by the slit.
+.EE
+.EX 8
+.B i.e.
+#!/bin/sh
+speyes \-w & sleep 2
+gkrellm \-w & sleep 2
+.EE 
+.\"
+.\" ***** THE TOOLBAR *****
+.\"
+.TP
+.B The Toolbar
+The toolbar provides an alternate method for 
+cycling through multiple workspaces and 
+applications. The left side of the toolbar is 
+the workspace control, the center is the 
+application control, and the right side is a
+clock. The format of the clock can be controlled
+as described under RESOURCES.
+
+Mouse button 3 raises a menu that allows 
+configuration of the toolbar. It can be 
+positioned either at the top or the bottom 
+of the screen and can be set to auto hide 
+and/or to always be on top.
+
+.EX 
+.\" ----- toolbar caveat -----
+.B Caveat: 
+The toolbar is a permanent fixture. It 
+can only be removed by modifying the source and 
+rebuilding, which is beyond the scope of this 
+document. Setting the toolbar to auto hide is
+the next best thing.
+.EE
+
+.\"
+.\" ***** WINDOW DECORATIONS *****
+.\"
+.\" ----- overview -----
+.TP 
+.B Window Decorations
+Window decorations include handles at the bottom of
+each window, a title bar, and three control buttons.
+The handles at the bottom of the window are divided 
+into three sections.  The two corner sections are 
+resizing handles The center section is a window 
+moving handle. The bottom center handle and the 
+title bar respond to a number of mouse clicks and 
+key + mouse click combinations. The three buttons
+in the title bar, left to right, are iconify, maximize,
+and close. The resize button has special behavior 
+detailed below.
+.\"
+.\" ----- mouse buttons -----
+.\"
+.TP
+.BR "Button One" "  (Left Button)" 
+Click and drag on titlebar to move or resize from bottom corners.
+Click the iconify button to move the window to the icon list.
+Click the maximize button to fully maximize the window.
+Click the close button to close the window and application.
+Double-Click the title bar to shade the window.
+.TP
+.BR "Alt + Button One" ""
+Click anywhere on client window and drag to move the window.
+.TP
+.BR "Button Two" "  (Middle Button)"
+Click the titlebar to lower the window.
+Click the maximize button to maximize the window vertically.
+.TP
+.BR "Button Three" "  (Right Button)"
+Click on title bar or bottom center handle pulls down a control menu.
+Click the maximize button to maximize the window horizontally.
+.TP
+.BR "Alt + Button Three" ""
+Click anywhere on client window and drag to resize the window.
+
+.TP
+.\"
+.\" ----- control menu -----
+.\"
+.B The control menu contains:
+.TP
+.B Send To ...
+.EX
+.BR "Button One" "  (Left Button)"
+Click to send this window to another workspace.  
+.EE
+.EX
+.BR "Button Two" "  (Middle Button)"
+Click to send this window to another workspace, change 
+to that workspace and keep the application focused.
+as well.
+.TP
+.B Shade
+This is the same action as Double-Click with Button One.
+.TP
+.B Iconify
+Hide the window.  It can be accessed with the icon menu.
+.TP
+.B Maximize
+Toggle window maximization.
+.TP
+.B Raise
+Bring window to the front above the other windows and
+focus it.
+.TP
+.B Lower
+Drop the window below the other ones.
+.TP
+.B Stick
+Stick this window to the glass on the inside of
+the monitor so it does not hide when you change 
+workspaces.
+.TP
+.B Kill Client
+This kills the client program with \-SIGKILL (\-9)
+Only use this as a last resort.
+.TP
+.B Close
+Send a close signal to the client application.
+.\"
+.\" * * * * * STYLES * * * * * 
+.\"
+.\" ----- overview -----
+.SH STYLES
+Styles are a collection of colors, fonts,
+and textures that control the appearance of
+Blackbox. These characteristics are recorded
+in style files. The default system style files
+are located in
+.I @pkgdatadir@/styles.
+The menu system will identify the style by 
+its filename, and styles can be sorted into
+different directories at the user's discretion.
+
+.\" ----- third party styles -----
+There are over 700 styles available for 
+Blackbox. The official distribution point for 
+Blackbox styles is
+
+.EX
+.B http://blackbox.themes.org/
+.EE
+
+.\"
+.\" ----- installing styles -----
+.\"
+All themes should install by simply downloading them 
+to 
+.B ~/.blackbox/
+then unzip it, and de-tar it.
+
+On open Unixes this will be:
+
+.B tar zxvf stylename.tar.gz
+
+On commercial Unixes this will be something like:
+
+.B gunzip stylename.tar.gz && tar xvf stylename.tar
+
+Check your system manuals for specifics or check with
+your network administrator.
+
+An entry should appear in the styles menu immediately.
+.EX
+.B Security Warning
+Style files can execute shell scripts and other
+executables. It would is wise to check the 
+rootCommand in the style file and make sure that 
+it is benign.
+.EE
+.TP
+.B Things that go wrong.
+.TP
+1. The theme is pre Blackbox 0.51.
+Style file syntax changed with version 0.51
+.TP
+2. The style tarball was formatted incorrectly.
+Some styles use the directories 
+.B ~/.blackbox/Backgrounds 
+and
+.B ~/.blackbox/Styles
+
+This can fixed by adding a 
+.B [stylemenu] (~/.blackbox/Styles)
+to your menu file. To be a complete purist, hack 
+the style file with the correct paths and move
+the files into the correct directories
+.TP
+3. The rootCommmand line is broken.
+The rootCommand line in the style file will run an 
+arbitrary executable. It is important that this
+executable be set to bsetbg to maintain portability 
+between systems with different graphics software. In
+addition bsetbg can execute a shell script and do it
+in a portable fashion as well. 
+.\"
+.\" ----- style format ------
+.\"
+.TP
+.B The documented method for creating styles is as follows:
+.\" ----- background image -----
+.TP
+1. Create or acquire the background for the style if
+it will not be using 
+.B bsetroot 
+to draw a patterned background for the root window. 
+
+.EX
+.B NOTE:
+Blackbox runs on a wide variety 
+of systems ranging from PCs with 640x480 256 color 
+displays to ultra high speed workstations with 25" 
+screens and extreme resolution. For best results a 
+style graphic should be at least 1024x768.
+.EE 
+.\" ----- style file ------
+.TP
+2. Create a style file. 
+The best way to do this is to make a copy of a 
+similar style and then edit it. 
+
+The style file is a list of X resources and other
+external variables. Manipulating these variables 
+allows users to completely change the appearance 
+of Blackbox. The user can also change the root 
+window image by using the wrapper program 
+.B bsetbg.
+
+bsetbg knows how to use a number of programs to
+set the root window image. This makes styles much
+more portable since various platforms have different 
+graphics software. For more info see 
+.B bsetbg (1).
+.\" ----- directory layout
+.TP
+3. Background images should be placed in
+.B ~/.blackbox/backgrounds
+The style file should be placed in
+.B ~/.blackbox/styles
+any other information about the style should 
+be placed in 
+.B ~/.blackbox/about/STYLE_NAME/.
+This would include README files, licenses, etc.
+
+Previous versions of Blackbox put backgrounds 
+and styles in different directories. The 
+directories listed above are the only officially 
+supported directories.  However you may put them
+whereever you like as long as you update your menu
+file so it knows where to find your styles.
+.\" ----- tarball -----
+.TP
+4. To create a consistent experience and to ensure
+portability between all systems it is important 
+to use the following format to create your style
+archive.
+
+first create a new directory named 
+after your style
+.B NEW_STYLE
+
+In this directory create the 
+directories
+.EX
+.B backgrounds
+.B styles
+.B about/NEW_STYLE
+.EE
+Next put everything for the theme 
+in these locations. Finally type
+
+tar cvzf NEW_STYLE.tar.gz *
+
+If you are using commercial Unix you may 
+need to use gzip and tar separately.
+
+Now when a user downloads a new style file
+she knows that all she has to do is put
+the tarball in her Blackbox directory,
+unzip->un-tar it and then click on it in her
+style menu.
+.TP
+.B
+.\" ----- X resources -----
+.SH Style File Syntax and Details
+
+By far the easiest way to create a new style is to 
+use bbconf. bbconf allows complete control of every 
+facet of style files and gives immediate updates of
+the current style as changes are made.
+
+The style file format is not currently documented in
+a man page.  There is a readme document included with
+the Blackbox source containing this information.
+.\"
+.\" * * * * * MENU FILE * * * * *
+.\"
+.\" ----- overview -----
+.SH MENU FILE
+The default menu file is installed in 
+.B @defaultmenu@.
+This menu can be customized as a system 
+default menu or the user can create a 
+personal menu.
+
+To create a personal menu copy the 
+default menu to a file in your home directory.
+Then, open 
+.B ~/.blackboxrc 
+and add or modify the resource
+.BI "session.menuFile:" "  ~/.blackbox/menu"
+
+Next, edit the new menu file. This can be done 
+during a Blackbox session and the menu will 
+automatically be updated when the code checks
+for file changes.
+
+The default menu included with Blackbox has 
+numerous comments describing the use of all 
+menu commands. Menu commands follow this general 
+form:
+
+.BI "[command]" "  (label|filename) {shell command|filename}" 
+.\"
+.\" ----- menu commands -----
+.\"
+.TP
+.B Blackbox menu commands:
+.TP
+.BI "   #    " "string..."
+Hash (or pound or number sign) is used as the comment delimiter. It can
+be used as a full line comment or as an end of 
+line comment after a valid command statement.
+.TP
+.BI "[begin]" "  (string)"
+This tag is used only once at the beginning of the 
+menu file. "string" is the name or description used
+at the top of the menu.
+.TP
+.BI "[end]       " "" 
+This tag is used at the end of the menu file
+and at the end of a submenu block.
+.TP
+.BI "[exec]" "  (label string) {command string}"
+This is a very flexible tag that allows the user
+to run an arbitrary shell command including shell
+scripts. If a command is too large to type on the 
+command line by hand it is best to put it in a 
+shell script. 
+.TP
+.BI "[nop]" "  (label string)"
+This tag is used to put a divider in the menu.
+.I label string
+is an optional description.
+.TP
+.BI "[submenu]" "  (submenu name) {title string}"
+This creates a sub-menu with the name
+.I submenu name
+and if given, the string
+.I title string
+will be the title of the pop up menu itself.
+.TP
+.BI "[include]" "  (filename)"
+This command inserts
+.I filename
+into the menu file at the point at which it is
+called. 
+.I filename
+should not contain a begin end pair. This feature
+can be used to include the system menu or include a
+piece of menu that is updated by a separate program.
+.TP
+.BI "[stylesdir]" "  (description) (path)"
+Causes Blackbox to search 
+.I path
+for style files. Blackbox lists styles in the menu
+by their file name as returned by the OS.
+.TP
+.BI "[stylesmenu]" "  (description) {path}"
+This command creates a submenu with the name
+.B description
+with the contents of 
+.B path.
+By creating a submenu and then populating it 
+with stylesmenu entries the user can create an
+organized library of styles.
+.TP
+.BI "[workspaces]" "  (description)"
+Inserts a link into the main menu to the workspace
+menu. If used,
+.I description
+is an optional description.
+.TP
+.BI "[config]" "  (label)"
+This command causes Blackbox to insert a menu that
+gives the user control over focus models, dithering
+and other system preferences.
+.TP
+.BI "[reconfig]" "  (label) {shell command}"
+The reconfig command causes Blackbox to reread its 
+configuration files. This does not include 
+.B ~/.blackboxrc
+which is only reread when Blackbox is restarted. If
+.I shell command
+is included Blackbox will run this command or 
+shell script before rereading the files. This can 
+be used to switch between multiple configurations
+.TP
+.BI "[restart]" "  (label) {shell command}"
+This command is actually an exit command that
+defaults to restarting Blackbox. If provided
+.B shell command
+is run instead of Blackbox. This can be used
+to change versions of Blackbox. Not that you would
+ever want to do this but, it could also be used
+to start a different window manager. 
+.TP
+.BI "[exit]" "  (label)"
+Shuts down Blackbox. If Blackbox is the last command in your
+.B ~/.xinitrc
+file, this action will also shutdown X. 
+.EX 
+.B Here is a working example of a menu file:
+.\" ----- menu example -----
+[begin] (MenuName)
+   [exec] (xterm) {xterm \-ls \-bg black \-fg green}
+   [submenu] (X utilities)
+      [exec] (xcalc) {xcalc}
+   [end]
+   [submenu] (styles)
+      [stylesmenu] (built-in styles) {@pkgdatadir@/styles}
+      [stylesmenu] (custom styles) {~/.blackbox/styles}
+   [end]
+   [workspaces] (workspace list)
+   [config] (configure)
+   [reconfig] (config play desktop) {play-config-blackbox}
+   [reconfig] (config work desktop) {work-config-blackbox}
+   [restart] (start Blackbox beta 7) {blackbox-beta7}
+   [restart] (start Blackbox cvs) {blackbox-cvs}
+   [restart] (restart)
+   [exit] (exit)
+[end]
+.EE
+.\"
+.\" * * * * * RESOURCE FILE * * * * * 
+.\"
+.SH RESOURCE FILE     
+.BI "$HOME" "/.blackboxrc"
+.\" ----- overview -----
+These options are stored in the ~/.blackboxrc file.
+They control various features of Blackbox and most
+can be set from menus. Some of these can
+only be set by editing .blackboxrc directly.
+
+NOTE: Blackbox only reads this file during start
+up. To make changes take effect during a Blackbox
+session the user must choose "restart" on the main menu.
+If you do not do so, your changes will be lost when
+Blackbox exits.
+
+Some resources are named with a <num> after screen. This
+should be replaced with the number of the screen
+that is being configured. The default is 0 (zero).
+.\" ----- resource keys -----
+.\"
+.\" ***** MENU CONFIGURABLE FROM SLIT MENU *****
+.\"
+.TP 3
+.IB "Menu Configurable" "  (Slit Menu):"
+Right click (button 3) on the slit border.
+.TP 3
+.BI "session.screen<num>.slit.placement" "  SEE BELOW"
+Determines the position of the slit.
+Certain combinations of slit.placement with
+slit.direction are not terribly useful, i.e. TopCenter
+with Vertical direction puts the slit through the
+middle of your screen. Certainly some will think that
+is cool if only to be different...
+.EX
+.B Default is CenterLeft.
+[  TopLeft  |   TopCenter  |   TopRight  | 
+ CenterLeft |              | CenterRight |
+ BottomLeft | BottomCenter | BottomRight ]
+.EE
+.TP 3
+.BI "session.screen<num>.slit.direction" "  [Horizontal|Vertical]"
+Determines the direction of the slit.
+.EX
+.B Default is Vertical.
+.EE
+.TP 3
+.BI "session.screen<num>.slit.onTop" "  [True|False]"
+Determines whether the slit is always visible
+over windows or if the focused window can hide
+the slit.
+.EX 
+.B Default is True.
+.EE
+.TP 3
+.BI "session.screen<num>.slit.autoHide" "  [True|False]"
+Determines whether the slit hides when not in use.
+The session.autoRaiseDelay time determines how long you
+must hover to get the slit to raise and how long it
+stays visible after mouse out.
+.EX
+.B Default is False.
+.EE
+.\"
+.\" ***** MENU CONFIGURABLE FROM MAIN MENU *****
+.\"
+.TP 3
+.IB "Menu Configurable" "  (Main Menu):"
+.TP 3
+.BI "session.screen<num>.focusModel" "  SEE BELOW"
+Sloppy focus (mouse focus) is the conventional X Window
+behavior and can be modified with AutoRaise or
+Click-Raise.
+
+AutoRaise causes the window to automatically raise after
+session.autoRaiseDelay milliseconds.
+
+ClickRaise causes the window to raise if you click
+anywhere inside the client area of the window.
+
+Sloppy focus alone requires a click on the titlebar,
+border or lower grip to raise the window.
+
+ClickToFocus requires a click on a Blackbox decoration
+or in the client area to focus and raise the window.
+ClickToFocus cannot be modified by AutoRaise or 
+ClickRaise.
+.EX
+.B Default is SloppyFocus
+[SloppyFocus [[AutoRaise & ClickRaise]  |
+              [AutoRaise | ClickRaise]] | 
+ClickToFocus]
+.EE
+.TP 3
+.BI "session.screen<num>.windowPlacement" "  SEE BELOW"
+RowSmartPlacement tries to fit new windows in empty space
+by making rows.
+Direction depends on session.screen<num>.rowPlacementDirection
+
+ColSmartPlacement tries to fit new windows in empty space
+by making columns
+Direction depends on session.screen<num>.colPlacementDirection
+
+CascadePlacement places the new window down and to
+the right of the most recently created window.
+.EX
+.B Default is RowSmartPlacement.
+[RowSmartPlacement | ColSmartPlacement | CascadePlacement]
+.EE
+.TP 3
+.BI "session.screen<num>.rowPlacementDirection" "  [LeftToRight|RightToLeft]"
+Determines placement direction for new windows.
+.EX
+.B Default is LeftToRight.
+.EE
+.TP 3
+.BI "session.screen<num>.colPlacementDirection" "  [TopToBottom|BottomToTop]"
+Determines placement direction for new windows.
+.EX
+.B Default is TopToBottom.
+.EE
+.TP 3
+.BI "session.imageDither" "  [True|False]" 
+This setting is only used when running in low 
+color modes. Image Dithering helps one to show an
+image properly even if there are not enough
+colors available in the system.
+.EX 
+.B Default is False.
+.EE
+.TP 3
+.BI "session.opaqueMove" "  [True|False]"
+Determines whether the window's contents are drawn as it is moved.  When
+False the behavior is to draw a box representing the window.
+.EX
+.B Default is False.
+.EE
+.TP 3
+.BI "session.screen<num>.fullMaximization" "  [True|False]"
+Determines if the maximize button will cause an application
+to maximize over the slit and toolbar.
+.EX 
+.B Default is False.
+.EE
+.TP 3
+.BI "session.screen<num>.focusNewWindows" "  [True|False]"
+Determines if newly created windows are given focus after
+they initially draw themselves.
+.EX
+.B Default is False. 
+.EE 
+.TP
+.BI "session.screen<num>.focusLastWindow" "  [True|False]"
+This is actually "when moving between workspaces, remember
+which window has focus when leaving a workspace and return
+the focus to that window when I return to that workspace."
+.EX
+.B Default is False.
+.EE
+.TP
+.BI "session.screen<num>.disableBindingsWithScrollLock" "  [True|False]"
+When this resource is enabled, turning on scroll lock
+keeps Blackbox from grabbing the Alt and Ctrl keys
+that it normally uses for mouse controls. This feature
+allows users of drawing and modeling programs which use
+keystrokes to modify mouse actions to maintain their sanity.
+*NOTE* this has _no_ affect on bbkeys.  If you need bbkeys to also
+behave this way it has a similar option in its config file.  Refer
+to the bbkeys manpage for details.
+.EX
+.B Default is False.
+.EE
+.\"
+.\" ***** MENU CONFIGURABLE FROM WORKSPACE MENU *****
+.\"
+.TP
+.IB "Menu Configurable" "  (Workspace Menu):"
+Middle click (button 2) on the root window (AKA Desktop) 
+to reach this menu
+.TP 3
+.BI "session.screen<num>.workspaces" "  [integer]"
+Workspaces may be created or deleted by middle clicking
+on the desktop and choosing "New Workspace" or "Remove
+Last". After creating a workspace, right click on the
+toolbar to name it.
+.EX
+.B Default is 1
+.EE
+.\"
+.\" ***** MENU CONFIGURABLE FROM TOOLBAR MENU *****
+.\"
+.TP
+.IB "Menu Configurable" "  (Toolbar Menu):"
+.TP 3
+.BI "session.screen<num>.workspaceNames" "  [string[, string...]]"
+Workspaces are named in the order specified in this
+resource. Names should be delimited by commas. If there
+are more workspaces than explicit names, un-named 
+workspaces will be named as "Workspace [number]".
+.EX
+.B Default is 
+Workspace 1.
+.EE
+.TP 3
+.BI "session.screen<num>.toolbar.placement" "  SEE BELOW"
+Set toolbar screen position.
+.EX 
+.B Default is BottomCenter
+[  TopLeft  |   TopCenter  |   TopRight  | 
+ BottomLeft | BottomCenter | BottomRight ]
+.EE
+.TP 3
+.BI "session.screen<num>.toolbar.onTop" "  [True|False]"
+Determines whether the toolbar is always visible
+over windows or if the focused window can hide
+the toolbar.
+.EX
+.B Default is True.
+.EE
+.TP 3
+.BI "session.screen<num>.toolbar.autoHide" "  [True|False]"
+Determines whether the toolbar hides when not in use.
+The session.autoRaiseDelay time determines how long you
+must hover to get the toolbar to raise, and how long it
+stays visible after mouse out.
+.EX
+.B Default is False.
+.EE
+.\"
+.\" ***** CONFIGURABLE IN BLACKBOXRC ONLY *****
+.\"
+.TP 3
+.IB "Configurable in" "  ~/.Blackboxrc only:"
+.TP 3
+.BI "session.screen<num>.toolbar.widthPercent" "  [1-100]"
+Percentage of screen used by the toolbar.
+A number from 1-100 that sets the width of the toolbar.
+0 (zero) does not cause the toolbar to disappear, instead
+the toolbar is set to the default. If you want to lose the
+toolbar there are patches that can remove it.
+.EX
+.B Default is 66.
+.EE
+.TP 3
+.BI "session.screen<num>.strftimeFormat" "  [string]"
+A C language date format string, any combination of
+specifiers can be used. The default is %I:%M %p which
+generates a 12 hour clock with minutes and an am/pm
+indicator appropriate to the locale.
+.EX
+.IB "24 hours and minutes" "    %H:%M"
+.IB "12 hours and minute" "     %I:%M %p"
+.IB "month/day/year" "          %m/%d/%y"
+.IB "day/month/year" "          %d/%m/%y"
+.EE
+.EX
+.B Default is hours:minutes am/pm
+See
+.B strftime 3
+for more details.
+.EE
+.TP 3
+.BI "session.screen<num>.dateFormat" "  [American|European]"
+NOTE: Only used if the strftime() function is not
+available on  your system.
+.EX
+.B Default is American, (mon/day/year).
+.EE
+.TP 3
+.BI "session.screen<num>.clockFormat" "  [12/24]"
+.B NOTE: 
+Only used if the strftime() function is not
+available on your system.
+.EX 0
+.B Default is 12-hour format.
+.EE
+.TP 3
+.BI "session.screen<num>.edgeSnapThreshold" "  [integer]"
+When set to 0 this turns off edge snap. When set to one
+or greater edge snap will cause a window that is being
+moved to snap to the nearest screen edge, the slit, or
+or the toolbar. Windows will not snap to each other.
+The value represents a number in pixels which is the distance
+between the window and a screen edge which is required before
+the window is snapped to the screen edge.  If you prefer this
+functionality values between 6 - 10 work nicely.
+.EX
+.B Default value is 0
+.EE
+.TP 3
+.BI "session.menuFile" "  [filepath]"
+Full path to the current menu file.
+.EX
+.B Default is @defaultmenu@
+.EE
+.TP 3
+.BI "session.colorsPerChannel" "  [2-6]"
+The number of colors taken from the X server for use on
+pseudo color displays. This value must be set to 4 for
+8 bit displays.
+.EX
+.B Default is 4.
+.EE
+.TP 3
+.BI "session.doubleClickInterval" "  [integer]"
+This is the maximum time that Blackbox will wait after
+one click to catch a double click. This only applies to
+Blackbox actions, such as double click shading, not to the X
+server in general.
+.EX
+.B Default is 250 milliseconds.
+.EE
+.TP 3
+.BI "session.autoRaiseDelay" "  [integer]"
+This is the time in milliseconds used for auto raise
+and auto hide behaviors. More than about 1000 ms is
+likely useless.
+.EX
+.B Default is 250 millisecond.
+.EE
+.TP 3
+.BI "session.cacheLife" "  [integer]"
+Determines the maximum number of minutes that the X server
+will cache unused decorations.
+.EX 
+.B Default is 5 minutes
+.EE
+.TP 3
+.BI "session.cacheMax" "  [integer]"
+Determines how many kilobytes that Blackbox may take 
+from the X server for storing decorations. Increasing 
+this number may enhance your performance if you have 
+plenty of memory and use lots of different windows.
+.EX 
+.B Default is 200 Kilobytes
+.EE
+
+.\"
+.\" * * * * * ENVIRONMENT * * * * * 
+.\"
+.SH ENVIRONMENT
+.TP
+.B HOME
+Blackbox uses $HOME to find its .blackboxrc  
+rc file and its .blackbox directory for menus
+and style directories.
+.TP
+.B DISPLAY
+If a display is not specified on the command line, 
+Blackbox will use the value of $DISPLAY.
+
+.\"
+.\" * * * * * FILES * * * * * 
+.\"
+.SH FILES
+.TP
+.B blackbox
+Application binary
+.TP
+.B ~/.blackboxrc
+User's startup and resource file.
+.TP
+.B @defaultmenu@
+Default system wide menu
+	
+.\"
+.\" * * * * * WEB SITES * * * * * 
+.\"
+.SH WEB SITES
+.TP 5
+.\" ----- general info -----
+.B General info website:
+http://blackboxwm.sourceforge.net/
+.TP 5
+.\" ----- development info -----
+.B Development website:
+http://sourceforge.net/projects/blackboxwm/
+
+.\"
+.\" * * * * * BUGS * * * * * 
+.\"
+.SH BUGS
+If you think you have found a bug, please help by going
+to the development website and select "Bugs" in the upper
+menu. Check the bug list to see if your problem has already
+been reported. If it has please read the summary and add any
+information that you believe would help. If your bug has not been 
+submitted select "Submit New" and fill out the form.
+
+.\"
+.\" * * * * * AUTHORS AND HISTORY * * * * * 
+.\"
+.SH AUTHORS AND HISTORY 
+.\" ----- software authors -----
+.B Sean "Shaleh" Perry 
+.I " <shaleh@debian.org>" 
+is the current maintainer and is actively working 
+together with Brad to keep Blackbox up-to-date and 
+stable as a rock.
+
+.BI "Brad Hughes" "  <bhughes@trolltech.com>"
+originally designed and coded Blackbox in 1997 with 
+the intent of creating a memory efficient window 
+manager with no dependencies on external libraries. 
+Brad's original idea has become a popular alternative 
+to other window managers.
+
+.BI "Jeff Raven" "  <jraven@psu.edu>"
+then picked up the torch for the 0.61.x series after 
+Brad took a full time job at TrollTech.
+
+.\" ----- man page author -----
+This manual page was written by:
+.B R.B. "Brig" Young 
+.I " <secretsaregood@yahoo.com>" 
+he is solely responsible for errors or omissions. 
+Comments, corrections, and suggestions are welcomed.
+
+.\"
+.\" * * * * * SEE ALSO * * * * * 
+.\"
+.SH SEE ALSO
+.EX 
+bsetbg(1), bsetroot(1), 
+bbkeys(1), bbconf(1)
+.EE 
diff --git a/.pc/120_include-ctime-header.patch/src/Toolbar.cc b/.pc/120_include-ctime-header.patch/src/Toolbar.cc
new file mode 100644
index 0000000..1e87b99
--- /dev/null
+++ b/.pc/120_include-ctime-header.patch/src/Toolbar.cc
@@ -0,0 +1,811 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+// Toolbar.cc for Blackbox - an X11 Window manager
+// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
+// Copyright (c) 1997 - 2000, 2002 - 2005
+//         Bradley T Hughes <bhughes at trolltech.com>
+//
+// 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
+// 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 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+
+#include "Toolbar.hh"
+#include "Iconmenu.hh"
+#include "Screen.hh"
+#include "Slit.hh"
+#include "Toolbarmenu.hh"
+#include "Window.hh"
+#include "Windowmenu.hh"
+#include "Workspacemenu.hh"
+
+#include <Pen.hh>
+#include <PixmapCache.hh>
+#include <Unicode.hh>
+
+#include <X11/Xutil.h>
+#include <sys/time.h>
+#include <assert.h>
+
+
+long nextTimeout(int resolution)
+{
+  timeval now;
+  gettimeofday(&now, 0);
+  return (std::max((time_t)1000, ((((resolution - (now.tv_sec % resolution)) * 1000))
+                           - (now.tv_usec / 1000))));
+}
+
+
+Toolbar::Toolbar(BScreen *scrn) {
+  _screen = scrn;
+  blackbox = _screen->blackbox();
+
+  // get the clock updating every minute
+  clock_timer = new bt::Timer(blackbox, this);
+  clock_timer->recurring(True);
+
+  const ToolbarOptions &options = _screen->resource().toolbarOptions();
+
+  const std::string &time_format = options.strftime_format;
+  if (time_format.find("%S") != std::string::npos
+      || time_format.find("%s") != std::string::npos
+      || time_format.find("%r") != std::string::npos
+      || time_format.find("%T") != std::string::npos) {
+    clock_timer_resolution = 1;
+  } else if (time_format.find("%M") != std::string::npos
+             || time_format.find("%R") != std::string::npos) {
+    clock_timer_resolution = 60;
+  } else {
+    clock_timer_resolution = 3600;
+  }
+
+  hide_timer = new bt::Timer(blackbox, this);
+  hide_timer->setTimeout(blackbox->resource().autoRaiseDelay());
+
+  setLayer(options.always_on_top
+           ? StackingList::LayerAbove
+           : StackingList::LayerNormal);
+  hidden = options.auto_hide;
+
+  new_name_pos = 0;
+
+  display = blackbox->XDisplay();
+  XSetWindowAttributes attrib;
+  unsigned long create_mask = CWColormap | CWOverrideRedirect | CWEventMask;
+  attrib.colormap = _screen->screenInfo().colormap();
+  attrib.override_redirect = True;
+  attrib.event_mask = ButtonPressMask | ButtonReleaseMask |
+                      EnterWindowMask | LeaveWindowMask | ExposureMask;
+
+  frame.window =
+    XCreateWindow(display, _screen->screenInfo().rootWindow(), 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.window, this);
+
+  attrib.event_mask =
+    ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask;
+
+  frame.workspace_label =
+    XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.workspace_label, this);
+
+  frame.window_label =
+    XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.window_label, this);
+
+  frame.clock =
+    XCreateWindow(display, frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.clock, this);
+
+  frame.psbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.psbutton, this);
+
+  frame.nsbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.nsbutton, this);
+
+  frame.pwbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.pwbutton, this);
+
+  frame.nwbutton =
+    XCreateWindow(display ,frame.window, 0, 0, 1, 1, 0,
+                  _screen->screenInfo().depth(), InputOutput,
+                  _screen->screenInfo().visual(),
+                  create_mask, &attrib);
+  blackbox->insertEventHandler(frame.nwbutton, this);
+
+  frame.base = frame.slabel = frame.wlabel = frame.clk = frame.button =
+ frame.pbutton = None;
+
+  _screen->addStrut(&strut);
+
+  reconfigure();
+
+  clock_timer->setTimeout(nextTimeout(clock_timer_resolution));
+  clock_timer->start();
+
+  XMapSubwindows(display, frame.window);
+  XMapWindow(display, frame.window);
+}
+
+
+Toolbar::~Toolbar(void) {
+  _screen->removeStrut(&strut);
+
+  XUnmapWindow(display, frame.window);
+
+  bt::PixmapCache::release(frame.base);
+  bt::PixmapCache::release(frame.slabel);
+  bt::PixmapCache::release(frame.wlabel);
+  bt::PixmapCache::release(frame.clk);
+  bt::PixmapCache::release(frame.button);
+  bt::PixmapCache::release(frame.pbutton);
+
+  blackbox->removeEventHandler(frame.window);
+  blackbox->removeEventHandler(frame.workspace_label);
+  blackbox->removeEventHandler(frame.window_label);
+  blackbox->removeEventHandler(frame.clock);
+  blackbox->removeEventHandler(frame.psbutton);
+  blackbox->removeEventHandler(frame.nsbutton);
+  blackbox->removeEventHandler(frame.pwbutton);
+  blackbox->removeEventHandler(frame.nwbutton);
+
+  // all children windows are destroyed by this call as well
+  XDestroyWindow(display, frame.window);
+
+  delete hide_timer;
+  delete clock_timer;
+}
+
+
+unsigned int Toolbar::exposedHeight(void) const {
+  const ToolbarOptions &options = _screen->resource().toolbarOptions();
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+  return (options.auto_hide ? style.hidden_height : style.toolbar_height);
+}
+
+
+void Toolbar::reconfigure(void) {
+  ScreenResource &resource = _screen->resource();
+  const ToolbarOptions &options = resource.toolbarOptions();
+  const ToolbarStyle &style = resource.toolbarStyle();
+
+  unsigned int width = (_screen->screenInfo().width() *
+                        options.width_percent) / 100;
+
+  const unsigned int border_width = style.toolbar.borderWidth();
+  const unsigned int extra =
+    style.frame_margin == 0 ? style.button.borderWidth() : 0;
+  frame.rect.setSize(width, style.toolbar_height);
+
+  int x, y;
+  switch (options.placement) {
+  case TopLeft:
+  case TopRight:
+  case TopCenter:
+    switch (options.placement) {
+    case TopLeft:
+      x = 0;
+      break;
+    case TopRight:
+      x = _screen->screenInfo().width() - frame.rect.width();
+      break;
+    default:
+      x = (_screen->screenInfo().width() - frame.rect.width()) / 2;
+      break;
+    }
+    y = 0;
+    frame.y_hidden = style.hidden_height - frame.rect.height();
+    break;
+
+  case BottomLeft:
+  case BottomRight:
+  case BottomCenter:
+  default:
+    switch (options.placement) {
+    case BottomLeft:
+      x = 0;
+      break;
+    case BottomRight:
+      x = _screen->screenInfo().width() - frame.rect.width();
+      break;
+    default:
+      x = (_screen->screenInfo().width() - frame.rect.width()) / 2;
+      break;
+    }
+    y = _screen->screenInfo().height() - frame.rect.height();
+    frame.y_hidden = _screen->screenInfo().height() - style.hidden_height;
+    break;
+  }
+
+  frame.rect.setPos(x, y);
+
+  updateStrut();
+
+  time_t ttmp = time(NULL);
+
+  unsigned int clock_w = 0u, label_w = 0u;
+
+  if (ttmp != -1) {
+    struct tm *tt = localtime(&ttmp);
+    if (tt) {
+      char t[1024];
+      int len = strftime(t, 1024, options.strftime_format.c_str(), tt);
+      if (len == 0) { // invalid time format found
+        // so use the default
+        const_cast<std::string &>(options.strftime_format) = "%I:%M %p";
+        len = strftime(t, 1024, options.strftime_format.c_str(), tt);
+      }
+      /*
+       * find the length of the rendered string and add room for two extra
+       * characters to it.  This allows for variable width output of the fonts.
+       * two 'w' are used to get the widest possible width
+       */
+      clock_w =
+        bt::textRect(_screen->screenNumber(), style.font,
+                     bt::toUnicode(t)).width() +
+        bt::textRect(_screen->screenNumber(), style.font,
+                     bt::toUnicode("ww")).width();
+    }
+  }
+
+  for (unsigned int i = 0; i < _screen->workspaceCount(); i++) {
+    width =
+      bt::textRect(_screen->screenNumber(), style.font,
+                   _screen->resource().workspaceName(i)).width();
+    label_w = std::max(label_w, width);
+  }
+
+  label_w = clock_w = std::max(label_w, clock_w) + (style.label_margin * 2);
+
+  unsigned int window_label_w =
+    (frame.rect.width() - (border_width * 2)
+     - (clock_w + (style.button_width * 4) + label_w
+        + (style.frame_margin * 8))
+     + extra*6);
+
+  XMoveResizeWindow(display, frame.window, frame.rect.x(),
+                    hidden ? frame.y_hidden : frame.rect.y(),
+                    frame.rect.width(), frame.rect.height());
+
+  // workspace label
+  frame.slabel_rect.setRect(border_width + style.frame_margin,
+                            border_width + style.frame_margin,
+                            label_w,
+                            style.label_height);
+  // previous workspace button
+  frame.ps_rect.setRect(border_width + (style.frame_margin * 2) + label_w
+                        - extra,
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // next workspace button
+  frame.ns_rect.setRect(border_width + (style.frame_margin * 3)
+                        + label_w + style.button_width - (extra * 2),
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // window label
+  frame.wlabel_rect.setRect(border_width + (style.frame_margin * 4)
+                            + (style.button_width * 2) + label_w - (extra * 3),
+                            border_width + style.frame_margin,
+                            window_label_w,
+                            style.label_height);
+  // previous window button
+  frame.pw_rect.setRect(border_width + (style.frame_margin * 5)
+                        + (style.button_width * 2) + label_w
+                        + window_label_w - (extra * 4),
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // next window button
+  frame.nw_rect.setRect(border_width + (style.frame_margin * 6)
+                        + (style.button_width * 3) + label_w
+                        + window_label_w - (extra * 5),
+                        border_width + style.frame_margin,
+                        style.button_width,
+                        style.button_width);
+  // clock
+  frame.clock_rect.setRect(frame.rect.width() - clock_w - style.frame_margin
+                           - border_width,
+                           border_width + style.frame_margin,
+                           clock_w,
+                           style.label_height);
+
+  XMoveResizeWindow(display, frame.workspace_label,
+                    frame.slabel_rect.x(), frame.slabel_rect.y(),
+                    frame.slabel_rect.width(), frame.slabel_rect.height());
+  XMoveResizeWindow(display, frame.psbutton,
+                    frame.ps_rect.x(), frame.ps_rect.y(),
+                    frame.ps_rect.width(), frame.ps_rect.height());
+  XMoveResizeWindow(display, frame.nsbutton,
+                    frame.ns_rect.x(), frame.ns_rect.y(),
+                    frame.ns_rect.width(), frame.ns_rect.height());
+  XMoveResizeWindow(display, frame.window_label,
+                    frame.wlabel_rect.x(), frame.wlabel_rect.y(),
+                    frame.wlabel_rect.width(), frame.wlabel_rect.height());
+  XMoveResizeWindow(display, frame.pwbutton,
+                    frame.pw_rect.x(), frame.pw_rect.y(),
+                    frame.pw_rect.width(), frame.pw_rect.height());
+  XMoveResizeWindow(display, frame.nwbutton,
+                    frame.nw_rect.x(), frame.nw_rect.y(),
+                    frame.nw_rect.width(), frame.nw_rect.height());
+  XMoveResizeWindow(display, frame.clock,
+                    frame.clock_rect.x(), frame.clock_rect.y(),
+                    frame.clock_rect.width(), frame.clock_rect.height());
+
+  frame.base =
+    bt::PixmapCache::find(_screen->screenNumber(), style.toolbar,
+                          frame.rect.width(), frame.rect.height(),
+                          frame.base);
+  frame.slabel =
+    bt::PixmapCache::find(_screen->screenNumber(), style.slabel,
+                          frame.slabel_rect.width(),
+                          frame.slabel_rect.height(),
+                          frame.slabel);
+  frame.wlabel =
+    bt::PixmapCache::find(_screen->screenNumber(), style.wlabel,
+                          frame.wlabel_rect.width(),
+                          frame.wlabel_rect.height(),
+                          frame.wlabel);
+  frame.clk =
+    bt::PixmapCache::find(_screen->screenNumber(), style.clock,
+                          frame.clock_rect.width(),
+                          frame.clock_rect.height(),
+                          frame.clk);
+  frame.button =
+    bt::PixmapCache::find(_screen->screenNumber(), style.button,
+                          style.button_width, style.button_width,
+                          frame.button);
+  frame.pbutton =
+    bt::PixmapCache::find(_screen->screenNumber(),
+                          style.pressed,
+                          style.button_width, style.button_width,
+                          frame.pbutton);
+
+  XClearArea(display, frame.window, 0, 0,
+             frame.rect.width(), frame.rect.height(), True);
+
+  XClearArea(display, frame.workspace_label, 0, 0,
+             label_w, style.label_height, True);
+  XClearArea(display, frame.window_label, 0, 0,
+             window_label_w, style.label_height, True);
+  XClearArea(display, frame.clock, 0, 0,
+             clock_w, style.label_height, True);
+
+  XClearArea(display, frame.psbutton, 0, 0,
+             style.button_width, style.button_width, True);
+  XClearArea(display, frame.nsbutton, 0, 0,
+             style.button_width, style.button_width, True);
+  XClearArea(display, frame.pwbutton, 0, 0,
+             style.button_width, style.button_width, True);
+  XClearArea(display, frame.nwbutton, 0, 0,
+             style.button_width, style.button_width, True);
+}
+
+
+void Toolbar::updateStrut(void) {
+  // left and right are always 0
+  strut.top = strut.bottom = 0;
+
+  switch(_screen->resource().toolbarOptions().placement) {
+  case TopLeft:
+  case TopCenter:
+  case TopRight:
+    strut.top = exposedHeight();
+    break;
+  default:
+    strut.bottom = exposedHeight();
+  }
+
+  _screen->updateStrut();
+}
+
+
+void Toolbar::redrawClockLabel(void) {
+  const ToolbarOptions &options = _screen->resource().toolbarOptions();
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+
+  bt::Rect u(0, 0, frame.clock_rect.width(), frame.clock_rect.height());
+  if (frame.clk == ParentRelative) {
+    bt::Rect t(-frame.clock_rect.x(), -frame.clock_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.clock, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(), style.clock,
+                    frame.clock, u, u, frame.clk);
+  }
+
+  time_t tmp = 0;
+  struct tm *tt = 0;
+  char str[1024];
+  if ((tmp = time(NULL)) == -1)
+    return; // should not happen
+  tt = localtime(&tmp);
+  if (! tt)
+    return; // ditto
+  if (! strftime(str, sizeof(str), options.strftime_format.c_str(), tt))
+    return; // ditto
+
+  bt::Pen pen(_screen->screenNumber(), style.clock_text);
+  bt::drawText(style.font, pen, frame.clock, u, style.alignment,
+               bt::toUnicode(str));
+}
+
+
+void Toolbar::redrawWindowLabel(void) {
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+
+  bt::Rect u(0, 0, frame.wlabel_rect.width(), frame.wlabel_rect.height());
+  if (frame.wlabel == ParentRelative) {
+    bt::Rect t(-frame.wlabel_rect.x(), -frame.wlabel_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.window_label, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(), style.wlabel,
+                    frame.window_label, u, u, frame.wlabel);
+  }
+
+  BlackboxWindow *foc = _screen->blackbox()->focusedWindow();
+  if (! foc || foc->screen() != _screen)
+    return;
+
+  bt::Pen pen(_screen->screenNumber(), style.wlabel_text);
+  bt::drawText(style.font, pen, frame.window_label, u,
+               style.alignment,
+               bt::ellideText(foc->title(), u.width(), bt::toUnicode("..."),
+                              _screen->screenNumber(), style.font));
+}
+
+
+void Toolbar::redrawWorkspaceLabel(void) {
+  const bt::ustring &name =
+    _screen->resource().workspaceName(_screen->currentWorkspace());
+  const ToolbarStyle &style = _screen->resource().toolbarStyle();
+
+  bt::Rect u(0, 0, frame.slabel_rect.width(), frame.slabel_rect.height());
+  if (frame.slabel == ParentRelative) {
+    bt::Rect t(-frame.slabel_rect.x(), -frame.slabel_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.workspace_label, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(), style.slabel,
+                    frame.workspace_label, u, u, frame.slabel);
+  }
+
+  bt::Pen pen(_screen->screenNumber(), style.slabel_text);
+  bt::drawText(style.font, pen, frame.workspace_label, u,
+               style.alignment, name);
+}
+
+
+void Toolbar::redrawPrevWorkspaceButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.ps_rect.x(), -frame.ps_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.psbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.psbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::leftArrow(_screen->screenNumber()),
+                 pen, frame.psbutton, u);
+}
+
+
+void Toolbar::redrawNextWorkspaceButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.ns_rect.x(), -frame.ns_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.nsbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.nsbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::rightArrow(_screen->screenNumber()),
+                 pen, frame.nsbutton, u);
+}
+
+
+void Toolbar::redrawPrevWindowButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.pw_rect.x(), -frame.pw_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.pwbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.pwbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::leftArrow(_screen->screenNumber()),
+                 pen, frame.pwbutton, u);
+}
+
+
+void Toolbar::redrawNextWindowButton(bool pressed) {
+  const ToolbarStyle &style =
+    _screen->resource().toolbarStyle();
+
+  Pixmap p = pressed ? frame.pbutton : frame.button;
+  bt::Rect u(0, 0, style.button_width, style.button_width);
+  if (p == ParentRelative) {
+    bt::Rect t(-frame.nw_rect.x(), -frame.nw_rect.y(),
+               frame.rect.width(), frame.rect.height());
+    bt::drawTexture(_screen->screenNumber(), style.toolbar,
+                    frame.nwbutton, t, u, frame.base);
+  } else {
+    bt::drawTexture(_screen->screenNumber(),
+                    pressed ? style.pressed : style.button,
+                    frame.nwbutton, u, u, p);
+  }
+
+  const bt::Pen pen(_screen->screenNumber(), style.foreground);
+  bt::drawBitmap(bt::Bitmap::rightArrow(_screen->screenNumber()),
+                 pen, frame.nwbutton, u);
+}
+
+
+void Toolbar::buttonPressEvent(const XButtonEvent * const event) {
+  if (event->state == Mod1Mask) {
+    if (event->button == 1)
+      _screen->raiseWindow(this);
+    else if (event->button == 2)
+      _screen->lowerWindow(this);
+    return;
+  }
+
+  if (event->button == 1) {
+    if (event->window == frame.psbutton)
+      redrawPrevWorkspaceButton(True);
+    else if (event->window == frame.nsbutton)
+      redrawNextWorkspaceButton(True);
+    else if (event->window == frame.pwbutton)
+      redrawPrevWindowButton(True);
+    else if (event->window == frame.nwbutton)
+      redrawNextWindowButton(True);
+    return;
+  }
+
+  if (event->window == frame.window_label) {
+    BlackboxWindow *focus = blackbox->focusedWindow();
+    if (focus && focus->screen() == _screen) {
+      if (event->button == 2) {
+        _screen->lowerWindow(focus);
+      } else if (event->button == 3) {
+        Windowmenu *windowmenu = _screen->windowmenu(focus);
+        windowmenu->popup(event->x_root, event->y_root,
+                          _screen->availableArea());
+      } else if (blackbox->resource().toolbarActionsWithMouseWheel() &&
+                 blackbox->resource().shadeWindowWithMouseWheel() &&
+                 focus->hasWindowFunction(WindowFunctionShade)) {
+        if ((event->button == 4) && !focus->isShaded()) {
+          focus->setShaded(true);
+		  } else if ((event->button == 5) && focus->isShaded()) {
+          focus->setShaded(false);
+        }
+      }
+    }
+    return;
+  }
+
+  if ((event->window == frame.pwbutton) || (event->window == frame.nwbutton)) {
+    if (blackbox->resource().toolbarActionsWithMouseWheel()) {
+      if (event->button == 4)
+        _screen->nextFocus();
+      else if (event->button == 5)
+        _screen->prevFocus();
+    }
+    // XXX: current-workspace window-list menu with button 2 or 3 here..
+    return;
+  }
+
+  // XXX: workspace-menu with button 2 or 3 on workspace-items (prev/next/label) here
+
+  // default-handlers (scroll through workspaces, popup toolbar-menu)
+  if (event->button == 3) {
+    _screen->toolbarmenu()->popup(event->x_root, event->y_root,
+                                  _screen->availableArea());
+    return;
+  }
+
+  if (blackbox->resource().toolbarActionsWithMouseWheel()) {
+    if (event->button == 4)
+      _screen->nextWorkspace();
+    else if (event->button == 5)
+      _screen->prevWorkspace();
+  }
+}
+
+
+void Toolbar::buttonReleaseEvent(const XButtonEvent * const event) {
+  if (event->button == 1) {
+    const ToolbarStyle &style =
+      _screen->resource().toolbarStyle();
+
+    if (event->window == frame.psbutton) {
+      redrawPrevWorkspaceButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width)) {
+        _screen->prevWorkspace();
+      }
+    } else if (event->window == frame.nsbutton) {
+      redrawNextWorkspaceButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width)) {
+        _screen->nextWorkspace();
+      }
+    } else if (event->window == frame.pwbutton) {
+      redrawPrevWindowButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width))
+        _screen->prevFocus();
+    } else if (event->window == frame.nwbutton) {
+      redrawNextWindowButton(False);
+
+      if (bt::within(event->x, event->y,
+                     style.button_width, style.button_width))
+        _screen->nextFocus();
+    } else if (event->window == frame.window_label)
+      _screen->raiseFocus();
+  }
+}
+
+
+void Toolbar::enterNotifyEvent(const XCrossingEvent * const /*unused*/) {
+  if (!_screen->resource().toolbarOptions().auto_hide)
+    return;
+
+  if (hidden) {
+    if (! hide_timer->isTiming())
+      hide_timer->start();
+  } else if (hide_timer->isTiming()) {
+    hide_timer->stop();
+  }
+}
+
+void Toolbar::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
+  if (!_screen->resource().toolbarOptions().auto_hide)
+    return;
+
+  if (hidden) {
+    if (hide_timer->isTiming())
+      hide_timer->stop();
+  } else if (! hide_timer->isTiming()) {
+    hide_timer->start();
+  }
+}
+
+
+void Toolbar::exposeEvent(const XExposeEvent * const event) {
+  if (event->window == frame.clock) redrawClockLabel();
+  else if (event->window == frame.workspace_label) redrawWorkspaceLabel();
+  else if (event->window == frame.window_label) redrawWindowLabel();
+  else if (event->window == frame.psbutton) redrawPrevWorkspaceButton();
+  else if (event->window == frame.nsbutton) redrawNextWorkspaceButton();
+  else if (event->window == frame.pwbutton) redrawPrevWindowButton();
+  else if (event->window == frame.nwbutton) redrawNextWindowButton();
+  else if (event->window == frame.window) {
+    bt::Rect t(0, 0, frame.rect.width(), frame.rect.height());
+    bt::Rect r(event->x, event->y, event->width, event->height);
+    bt::drawTexture(_screen->screenNumber(),
+                    _screen->resource().toolbarStyle().toolbar,
+                    frame.window, t, r & t, frame.base);
+  }
+}
+
+
+void Toolbar::timeout(bt::Timer *timer) {
+  if (timer == clock_timer) {
+    redrawClockLabel();
+
+    clock_timer->setTimeout(nextTimeout(clock_timer_resolution));
+  } else if (timer == hide_timer) {
+    hidden = ! hidden;
+    if (hidden)
+      XMoveWindow(display, frame.window,
+                  frame.rect.x(), frame.y_hidden);
+    else
+      XMoveWindow(display, frame.window,
+                  frame.rect.x(), frame.rect.y());
+  } else {
+    // this should not happen
+    assert(0);
+  }
+}
+
+
+void Toolbar::toggleAutoHide(void) {
+  bool do_auto_hide = !_screen->resource().toolbarOptions().auto_hide;
+
+  updateStrut();
+  if (_screen->slit())
+    _screen->slit()->reposition();
+
+  if (!do_auto_hide && hidden) {
+    // force the toolbar to be visible
+    if (hide_timer->isTiming()) hide_timer->stop();
+    hide_timer->fireTimeout();
+  }
+}
+
+
+void Toolbar::setPlacement(Placement place) {
+  ToolbarOptions &options =
+    const_cast<ToolbarOptions &>(_screen->resource().toolbarOptions());
+  options.placement = place;
+  reconfigure();
+
+  // reposition the slit as well to make sure it doesn't intersect the
+  // toolbar
+  if (_screen->slit())
+    _screen->slit()->reposition();
+
+  _screen->saveResource();
+}
diff --git a/.pc/applied-patches b/.pc/applied-patches
new file mode 100644
index 0000000..ea46789
--- /dev/null
+++ b/.pc/applied-patches
@@ -0,0 +1,12 @@
+010_pkgconfig-requires-private.patch
+020_fix-ftbfs-gcc-4.3.patch
+030_bsetbg-bash-shebang.patch
+040_textpropertytostring-unconditional.patch
+050_manpage-tidy-up.patch
+060_fix-spelling-errors.patch
+070_fix-ftbfs-x32.patch
+080_configure.ac.patch
+090_focus.patch
+100_Prevent-crashes-on-apps-disabled-minimize-or-maximiz.patch
+110_fix-spelling.patch
+120_include-ctime-header.patch
diff --git a/configure.ac b/configure.ac
index 5eda4c2..6d8b50e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,10 +11,10 @@ test "x$prefix" = "xNONE" && prefix="$ac_default_prefix"
 dnl Look in the most logical places for external libraries
 CPPFLAGS="$CPPFLAGS -I$prefix/include"
 LDFLAGS="$LDFLAGS -L$prefix/lib"
-if test "x$prefix" != "x/usr/local"; then
-  CPPFLAGS="$CPPFLAGS -I/usr/local/include"
-  LDFLAGS="$LDFLAGS -L/usr/local/lib"
-fi
+#if test "x$prefix" != "x/usr/local"; then
+#  CPPFLAGS="$CPPFLAGS -I/usr/local/include"
+#  LDFLAGS="$LDFLAGS -L/usr/local/lib"
+#fi
 
 dnl Locate required external software
 AC_PROG_CC
diff --git a/debian/changelog b/debian/changelog
index a0d8b26..1faeeb5 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,19 @@
+blackbox (0.70.1-39) unstable; urgency=medium
+
+  * QA upload.
+  * d/gbp.conf: Specify branch, debian/master
+  * d/not-installed: Exclude *.la files from all architectures.
+  * Add 120_include-ctime-header.patch. Closes: #1016273
+  * autopkgtest:
+    - Remove test that runs in the background.
+    - Add 'superficial' to the remaining two tests.
+  * d/control:
+    - Bump debhelper to 13.
+    - Update Standards-Version to 4.6.1
+  * d/lintian-overrides: It seems that lintian can't find this typo, removing.
+
+ -- Håvard F. Aasen <havard.f.aasen@pfft.no>  Fri, 29 Jul 2022 23:01:51 +0200
+
 blackbox (0.70.1-38) unstable; urgency=medium
 
   * QA upload.
diff --git a/debian/control b/debian/control
index e2318d8..b014b49 100644
--- a/debian/control
+++ b/debian/control
@@ -2,11 +2,11 @@ Source: blackbox
 Section: x11
 Priority: optional
 Maintainer: Debian QA Group <packages@qa.debian.org>
-Build-Depends: debhelper-compat (= 12),
+Build-Depends: debhelper-compat (= 13),
                libxext-dev,
                libxft-dev,
                libxt-dev
-Standards-Version: 4.5.0
+Standards-Version: 4.6.1
 Rules-Requires-Root: no
 Homepage: https://sourceforge.net/projects/blackboxwm/
 Vcs-Git: https://salsa.debian.org/debian/blackbox.git
diff --git a/debian/gbp.conf b/debian/gbp.conf
new file mode 100644
index 0000000..8f53891
--- /dev/null
+++ b/debian/gbp.conf
@@ -0,0 +1,2 @@
+[DEFAULT]
+debian-branch = debian/master
diff --git a/debian/lintian-overrides b/debian/lintian-overrides
deleted file mode 100644
index 9d762fc..0000000
--- a/debian/lintian-overrides
+++ /dev/null
@@ -1,2 +0,0 @@
-# Not a spelling error. It is a sequence of bytes in binary file.
-blackbox: spelling-error-in-binary usr/bin/blackbox whitH with
diff --git a/debian/not-installed b/debian/not-installed
index 99475be..01b2eea 100644
--- a/debian/not-installed
+++ b/debian/not-installed
@@ -1 +1 @@
-usr/lib/x86_64-linux-gnu/libbt.la
+usr/lib/*/libbt.la
diff --git a/debian/patches/120_include-ctime-header.patch b/debian/patches/120_include-ctime-header.patch
new file mode 100644
index 0000000..bb8aaa4
--- /dev/null
+++ b/debian/patches/120_include-ctime-header.patch
@@ -0,0 +1,22 @@
+From: =?utf-8?b?IkjDpXZhcmQgRi4gQWFzZW4i?= <havard.f.aasen@pfft.no>
+Date: Fri, 29 Jul 2022 22:33:41 +0200
+Subject: include ctime header
+
+Bug-Debian: https://bugs.debian.org/1016273
+Forwarded: no
+---
+ src/Toolbar.cc | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/Toolbar.cc b/src/Toolbar.cc
+index 1e87b99..08b1a98 100644
+--- a/src/Toolbar.cc
++++ b/src/Toolbar.cc
+@@ -38,6 +38,7 @@
+ #include <X11/Xutil.h>
+ #include <sys/time.h>
+ #include <assert.h>
++#include <ctime>
+ 
+ 
+ long nextTimeout(int resolution)
diff --git a/debian/patches/series b/debian/patches/series
index 155155d..ea46789 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -9,3 +9,4 @@
 090_focus.patch
 100_Prevent-crashes-on-apps-disabled-minimize-or-maximiz.patch
 110_fix-spelling.patch
+120_include-ctime-header.patch
diff --git a/debian/tests/control b/debian/tests/control
index c574856..98b1e26 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,6 +1,5 @@
 Test-Command: blackbox -help
+Restrictions: superficial
 
 Test-Command: blackbox -version
-
-Test-Command: xvfb-run -a startx &
-Depends: @, xauth, xvfb, xorg
+Restrictions: superficial
diff --git a/doc/blackbox.1.in b/doc/blackbox.1.in
index 870dce6..0695246 100644
--- a/doc/blackbox.1.in
+++ b/doc/blackbox.1.in
@@ -251,7 +251,7 @@ learn more about dockapps refer to the web sites
 mentioned in the Description. Dockapps 
 automatically run in the slit in most cases, but
 may require a special command switch.  
-Often, -w is used for "withdrawn" into the slit.
+Often, \-w is used for "withdrawn" into the slit.
 
 gkrellm is a very useful and modern dockapp that
 gives the user near real time information on 
@@ -280,8 +280,8 @@ in the correct order by the slit.
 .EX 8
 .B i.e.
 #!/bin/sh
-speyes -w & sleep 2
-gkrellm -w & sleep 2
+speyes \-w & sleep 2
+gkrellm \-w & sleep 2
 .EE 
 .\"
 .\" ***** THE TOOLBAR *****
@@ -393,7 +393,7 @@ the monitor so it does not hide when you change
 workspaces.
 .TP
 .B Kill Client
-This kills the client program with -SIGKILL (-9)
+This kills the client program with \-SIGKILL (\-9)
 Only use this as a last resort.
 .TP
 .B Close
@@ -527,7 +527,7 @@ Previous versions of Blackbox put backgrounds
 and styles in different directories. The 
 directories listed above are the only officially 
 supported directories.  However you may put them
-whereever you like as long as you update your menu
+wherever you like as long as you update your menu
 file so it knows where to find your styles.
 .\" ----- tarball -----
 .TP
@@ -704,7 +704,7 @@ file, this action will also shutdown X.
 .B Here is a working example of a menu file:
 .\" ----- menu example -----
 [begin] (MenuName)
-   [exec] (xterm) {xterm -ls -bg black -fg green}
+   [exec] (xterm) {xterm \-ls \-bg black \-fg green}
    [submenu] (X utilities)
       [exec] (xcalc) {xcalc}
    [end]
@@ -847,7 +847,7 @@ Determines placement direction for new windows.
 .TP 3
 .BI "session.imageDither" "  [True|False]" 
 This setting is only used when running in low 
-color modes. Image Dithering helps to show an
+color modes. Image Dithering helps one to show an
 image properly even if there are not enough
 colors available in the system.
 .EX 
diff --git a/doc/bsetbg.1 b/doc/bsetbg.1
index 0b2115a..3f1515a 100644
--- a/doc/bsetbg.1
+++ b/doc/bsetbg.1
@@ -112,25 +112,25 @@ This variable can specify the logfile to be used when $LOG_LAST_CMD is defined.
 The default is ~/.bsetbg_last_cmd .
 
 .TP
-As mentioned above, \fBbsetbg\fR will function perfectly for the majority of users without having a configuration file. Power users who want more control over \fBbsetbg\fR's behavior should run \fBbsetbg -g\fR and use the output to create a \fI~/.bsetbgrc\fR which may then be tweaked by hand.
+As mentioned above, \fBbsetbg\fR will function perfectly for the majority of users without having a configuration file. Power users who want more control over \fBbsetbg\fR's behavior should run \fBbsetbg \-g\fR and use the output to create a \fI~/.bsetbgrc\fR which may then be tweaked by hand.
 
 .SH EXAMPLES
 In this example, bsetbg will set the image in centered mode:
 
-    bsetbg -center foo.png
+    bsetbg \-center foo.png
 
 An example of the \fB-exec\fR argument:
 
-    bsetbg -exec xv -root -quit -rmode 5 -rbg rgb:2/2/2 \\
-	-center foo.png
+    bsetbg \-exec xv \-root \-quit \-rmode 5 \-rbg rgb:2/2/2 \\
+	\-center foo.png
 
 An example in which bsetbg creates a configuration file using xv and qiv:
 
-	bsetbg -g xv qiv > ~/.bsetbgrc
+	bsetbg \-g xv qiv > ~/.bsetbgrc
 
 An example of the use of the \fB-app\fR argument:
 
-    bsetbg  -app qiv "-o rgb:d6/c5/a2 -x" -c foo.png
+    bsetbg \-app qiv "\-o rgb:d6/c5/a2 \-x" \-c foo.png
 
 .SH AUTHOR
 The author of
diff --git a/lib/Image.cc b/lib/Image.cc
index ebce0b1..8fe43e7 100644
--- a/lib/Image.cc
+++ b/lib/Image.cc
@@ -45,6 +45,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <cstring>
+
 // #define COLORTABLE_DEBUG
 // #define MITSHM_DEBUG
 
diff --git a/lib/Resource.cc b/lib/Resource.cc
index 8240719..3c18dd5 100644
--- a/lib/Resource.cc
+++ b/lib/Resource.cc
@@ -30,6 +30,8 @@
 
 #include <stdio.h>
 
+#include <cstring>
+
 
 bt::Resource::Resource(void)
   : db(NULL)
diff --git a/lib/Util.hh b/lib/Util.hh
index fe8625b..0f5df33 100644
--- a/lib/Util.hh
+++ b/lib/Util.hh
@@ -25,6 +25,8 @@
 #ifndef __Util_hh
 #define __Util_hh
 
+#include <X11/Xutil.h>
+
 #include <limits.h>
 #include <string>
 
@@ -94,10 +96,8 @@ namespace bt {
 
   std::string tolower(const std::string &string);
 
-#ifdef _XUTIL_H_
   std::string textPropertyToString(::Display *display,
                                    ::XTextProperty& text_prop);
-#endif
 
 } // namespace bt
 
diff --git a/lib/XDG.cc b/lib/XDG.cc
index feabb92..d21c7d9 100644
--- a/lib/XDG.cc
+++ b/lib/XDG.cc
@@ -26,7 +26,7 @@
 #include "XDG.hh"
 
 #include <stdlib.h>
-
+#include <algorithm>
 
 // make sure directory names end with a slash
 static std::string terminateDir(const std::string &string)
diff --git a/lib/libbt.pc.in b/lib/libbt.pc.in
index a2a8869..06dddc4 100644
--- a/lib/libbt.pc.in
+++ b/lib/libbt.pc.in
@@ -5,7 +5,7 @@ libdir=@libdir@
 
 Name: Blackbox Toolbox
 Description: Utility class library for writing small applications
-Requires: @XFT_PKGCONFIG@
 Version: @VERSION@
+Requires.private: @XFT_PKGCONFIG@
 Libs: -L${libdir} -lbt @LDFLAGS@ @ICONV@ @LOCALE@
 Cflags: -I${includedir}/bt
diff --git a/nls/C/blackbox.m b/nls/C/blackbox.m
index 74d09f6..a64d6ad 100644
--- a/nls/C/blackbox.m
+++ b/nls/C/blackbox.m
@@ -1,6 +1,6 @@
 $set 13 #blackbox
 
 $ #NoManagableScreens
-# Blackbox::Blackbox: no managable screens found, aborting\n
+# Blackbox::Blackbox: no manageable screens found, aborting\n
 $ #MapRequest
 # Blackbox::process_event: MapRequest for 0x%lx\n
diff --git a/nls/ko_KR/blackbox.m b/nls/ko_KR/blackbox.m
index 74d09f6..4b0ecaf 100644
--- a/nls/ko_KR/blackbox.m
+++ b/nls/ko_KR/blackbox.m
@@ -1,6 +1,6 @@
 $set 13 #blackbox
 
 $ #NoManagableScreens
-# Blackbox::Blackbox: no managable screens found, aborting\n
+# Blackbox::Blackbox: no maneagable screens found, aborting\n
 $ #MapRequest
 # Blackbox::process_event: MapRequest for 0x%lx\n
diff --git a/nls/zh_TW/blackbox.m b/nls/zh_TW/blackbox.m
index 74d09f6..a64d6ad 100644
--- a/nls/zh_TW/blackbox.m
+++ b/nls/zh_TW/blackbox.m
@@ -1,6 +1,6 @@
 $set 13 #blackbox
 
 $ #NoManagableScreens
-# Blackbox::Blackbox: no managable screens found, aborting\n
+# Blackbox::Blackbox: no manageable screens found, aborting\n
 $ #MapRequest
 # Blackbox::process_event: MapRequest for 0x%lx\n
diff --git a/src/BlackboxResource.cc b/src/BlackboxResource.cc
index 27d1323..443563c 100644
--- a/src/BlackboxResource.cc
+++ b/src/BlackboxResource.cc
@@ -33,6 +33,8 @@
 #include <X11/Xutil.h>
 #include <X11/cursorfont.h>
 
+#include <cstring>
+
 
 BlackboxResource::BlackboxResource(const std::string& rc): rc_file(rc) {
   screen_resources = 0;
diff --git a/src/Screen.cc b/src/Screen.cc
index 838d168..cd91295 100644
--- a/src/Screen.cc
+++ b/src/Screen.cc
@@ -48,6 +48,8 @@
 #include <ctype.h>
 #include <dirent.h>
 
+#include <cstring>
+
 
 static bool running = true;
 static int anotherWMRunning(Display *, XErrorEvent *) {
diff --git a/src/ScreenResource.cc b/src/ScreenResource.cc
index 239ddc6..163f159 100644
--- a/src/ScreenResource.cc
+++ b/src/ScreenResource.cc
@@ -33,6 +33,8 @@
 
 #include <assert.h>
 
+#include <cstring>
+
 
 static const int iconify_width  = 9;
 static const int iconify_height = 9;
diff --git a/src/Toolbar.cc b/src/Toolbar.cc
index e88b8f6..08b1a98 100644
--- a/src/Toolbar.cc
+++ b/src/Toolbar.cc
@@ -38,14 +38,15 @@
 #include <X11/Xutil.h>
 #include <sys/time.h>
 #include <assert.h>
+#include <ctime>
 
 
 long nextTimeout(int resolution)
 {
   timeval now;
   gettimeofday(&now, 0);
-  return (std::max(1000l, ((((resolution - (now.tv_sec % resolution)) * 1000l))
-                           - (now.tv_usec / 1000l))));
+  return (std::max((time_t)1000, ((((resolution - (now.tv_sec % resolution)) * 1000))
+                           - (now.tv_usec / 1000))));
 }
 
 
diff --git a/src/Window.cc b/src/Window.cc
index b1361ae..9cc861a 100644
--- a/src/Window.cc
+++ b/src/Window.cc
@@ -2319,7 +2319,9 @@ void BlackboxWindow::iconify(void) {
       }
     }
   } else {
-    assert(hasWindowFunction(WindowFunctionIconify));
+    if (!hasWindowFunction(WindowFunctionIconify)) {
+        return;
+    }
   }
 
   _screen->addIcon(this);
@@ -2338,7 +2340,9 @@ void BlackboxWindow::iconify(void) {
 
 
 void BlackboxWindow::maximize(unsigned int button) {
-  assert(hasWindowFunction(WindowFunctionMaximize));
+  if (!hasWindowFunction(WindowFunctionMaximize)) {
+      return;
+  }
 
   // any maximize operation always unshades
   client.ewmh.shaded = false;
diff --git a/src/blackbox.cc b/src/blackbox.cc
index d750fe6..0e6df45 100644
--- a/src/blackbox.cc
+++ b/src/blackbox.cc
@@ -281,6 +281,7 @@ void Blackbox::process_event(XEvent *e) {
     bool lost_focus = true; // did the window really lose focus?
     bool no_focus = true;   // did another window get focus?
 
+    XSync(XDisplay(), False);
     XEvent event;
     if (XCheckIfEvent(XDisplay(), &event, scanForFocusIn, NULL)) {
       process_event(&event);
@@ -420,7 +421,7 @@ Blackbox::Blackbox(char **m_argv, const char *dpy_name,
   }
 
   if (managed == 0) {
-    fprintf(stderr, "%s: no managable screens found, exiting...\n",
+    fprintf(stderr, "%s: no manageable screens found, exiting...\n",
             applicationName().c_str());
     ::exit(3);
   }
diff --git a/src/main.cc b/src/main.cc
index 2c48e0d..a4f7da6 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -36,6 +36,8 @@
 
 #include <stdio.h>
 
+#include <cstring>
+
 
 static void showHelp(int exitval) {
   // print version - this should not be localized!
diff --git a/util/bsetbg b/util/bsetbg
index fa68e9d..8f17cde 100755
--- a/util/bsetbg
+++ b/util/bsetbg
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # Copyright (c) 2000-2002 Timothy M. King (tmk@lordzork.com)
 #
diff --git a/util/bsetroot.cc b/util/bsetroot.cc
index 9fe3c5d..d576410 100644
--- a/util/bsetroot.cc
+++ b/util/bsetroot.cc
@@ -27,11 +27,14 @@
 #include <Pen.hh>
 #include <Texture.hh>
 
+#include <cstdlib>
 #include <cctype>
 
 #include <X11/Xatom.h>
 #include <stdio.h>
 
+#include <cstring>
+
 
 // ignore all X errors
 static int x11_error(::Display *, XErrorEvent *)

Run locally

More details

Full run details