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 ¤t_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 *)