]> git.corax.cc Git - dwm/commitdiff
Add client indicators to highlight the focused client
authorMatthias Kruk <m@m10k.eu>
Sat, 1 May 2021 08:50:54 +0000 (17:50 +0900)
committerMatthias Kruk <m@m10k.eu>
Sat, 1 May 2021 08:50:54 +0000 (17:50 +0900)
If the border around clients is very thin (e.g. 1 or 2 pixels), it can
be hard to tell at a glance which one is the focused client. This commit
adds client indicators, which are a bars at the top (in the bookshelf
layout) and the right (in the bookstack layout), which provide easily
visible cues about the currently active client.
The bars may further be used in the future to draw information about
the active and inactive clients.

config.def.h
dwm.c

index 96a3cc502f30eae939f8b7ec31bf9fdcb136f294..632c8834fd4c95bd000f9f61cc5ca98331efd2d0 100755 (executable)
@@ -1,7 +1,7 @@
 /* See LICENSE file for copyright and license details. */
 
 /* appearance */
-static const char font[]            = "YOzNFb 8";
+static const char font[] = "青柳衡山フォントT 12";
 
 static struct color_set theme[PalLast] = {
         { /* PalNormal */
@@ -20,6 +20,10 @@ static struct color_set theme[PalLast] = {
                 .border     = "#d2d2d2",
                 .background = "#d2d2d2",
                 .foreground = "#ffffff"
+        }, { /* PalIndicator */
+                .border     = "#c25676",
+                .background = "#c25676",
+                .foreground = "#bb2323"
         }
 };
 
@@ -28,9 +32,11 @@ static const char dmenu_normal_fg[]   = "#000000";
 static const char dmenu_sel_bg[]      = "#bb2323";
 static const char dmenu_sel_fg[]      = "#ffffff";
 
-static const unsigned int borderpx    = 1;        /* border pixel of windows */
-static const unsigned int snap        = 32;       /* snap pixel */
-static const unsigned int bar_padding = 4;        /* vertical padding of the bar */
+static const unsigned int borderpx      = 1;        /* border pixel of windows */
+static const unsigned int snap          = 32;       /* snap pixel */
+static const unsigned int bar_padding   = 4;        /* vertical padding of the bar */
+static const unsigned int clind_height  = 32;
+static const unsigned int clind_padding = 8;
 
 /* tagging */
 static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" };
diff --git a/dwm.c b/dwm.c
index b9791a227b3b9bbbd07b495438c4e07e9c715d4b..abdd7a4c1df9d9f961e0bc777b6a0da918584185 100755 (executable)
--- a/dwm.c
+++ b/dwm.c
@@ -43,7 +43,7 @@
 #define ALLMASK                 (ShiftMask | ControlMask | ALLMODMASK)
 #define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask) & ALLMASK)
 #define ISVISIBLE(C)            ((C->workspace->viewer))
-#define LENGTH(X)               (sizeof X / sizeof X[0])
+#define LENGTH(X)               (sizeof(X) / sizeof(X[0]))
 #ifndef MAX
 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
 #endif
@@ -89,6 +89,7 @@ enum {
        PalSelected,
        PalNotSelected,
        PalOther,
+       PalIndicator,
        PalLast
 };
 
@@ -224,17 +225,16 @@ struct layout {
 struct monitor {
        char ltsymbol[16];
        int num;
-       int by;
        struct rect geom;
        struct rect win_geom;
 
-       unsigned int sellt;
-       unsigned int tagset[2];
        struct workspace *workspace;
        struct workspace *previous_workspace;
        struct monitor *next;
-       Window barwin;
-       const struct layout *lt[2];
+       Window statusbar;
+       struct client_indicator *h_clind;
+       struct client_indicator *v_clind;
+       const struct layout *layout;
 };
 
 struct workspace {
@@ -258,6 +258,25 @@ struct padding {
        int v;
 };
 
+#define CI_VERTICAL   0
+#define CI_HORIZONTAL 1
+
+struct client_indicator {
+       struct rect geom;
+       Window win;
+       XftDraw *draw;
+       GC gc;
+       struct monitor *monitor;
+       int orientation;
+};
+
+static struct client_indicator* client_indicator_new(struct monitor *monitor, int orientation);
+static void client_indicator_free(struct client_indicator **indicator);
+static void client_indicator_update_geometry(struct client_indicator *ci);
+static void client_indicator_update(struct client_indicator *ci);
+static void client_indicator_set_visible(struct client_indicator *ci, int visible);
+static void client_indicator_resize(struct client_indicator *ci);
+
 #define KBPTR_CENTER   0
 #define KBPTR_NORTH    (1 << 1)
 #define KBPTR_EAST     (1 << 2)
@@ -293,7 +312,8 @@ static int  monitor_has_focus(struct monitor *mon);
 static void monitor_focus(struct monitor *mon);
 static void monitor_free(struct monitor *mon);
 static int  monitor_is_floating(struct monitor *mon);
-static void monitor_resize_barwin(struct monitor *m);
+static void monitor_resize_statusbar(struct monitor *m);
+static void monitor_update_geometry(struct monitor *mon, int x, int y, int w, int h);
 
 static void client_move_to_workspace(struct client *client, struct workspace *workspace);
 
@@ -309,7 +329,8 @@ static void clientmessage(XEvent *e);
 static void client_configure(struct client *c);
 static void configurenotify(XEvent *e);
 static void configurerequest(XEvent *e);
-static struct monitor *createmon(void);
+static struct monitor *monitor_new(int num, int x, int y, int w, int h);
+static struct monitor *monitor_get(int num);
 static void destroynotify(XEvent *e);
 static void die(const char *errstr, ...);
 static struct monitor *dirtomon(int dir);
@@ -438,6 +459,151 @@ struct NumTags {
        char limitexceeded[LENGTH(tags) > 31 ? -1 : 1];
 };
 
+static void client_indicator_update(struct client_indicator *ci)
+{
+       struct client *focused;
+
+       XCopyArea(dpy, root, ci->win, ci->gc,
+                 ci->geom.x, ci->geom.y,
+                 ci->geom.w, ci->geom.h,
+                 0, 0);
+
+       focused = ci->monitor->workspace->focused;
+
+       if(focused) {
+               int x, y, w, h;
+
+               if(ci->orientation == CI_HORIZONTAL) {
+                       x = focused->geom.x - ci->geom.x;
+                       y = clind_padding;
+                       w = focused->geom.w + 2 * borderpx;
+                       h = clind_height - clind_padding;
+               } else {
+                       x = 0;
+                       y = focused->geom.y - ci->geom.y;
+                       w = clind_height - clind_padding;
+                       h = focused->geom.h + 2 * borderpx;
+               }
+
+               XSetForeground(dpy, ci->gc, dc.palette[PalIndicator].color[ColBG]);
+               XFillRectangle(dpy, ci->win, ci->gc,
+                              x, y, w, h);
+
+               XSetForeground(dpy, ci->gc, dc.palette[PalIndicator].color[ColFG]);
+               XDrawRectangle(dpy, ci->win, ci->gc,
+                              x, y, w - 1, h - 1);
+       }
+
+       XSync(dpy, False);
+
+       return;
+}
+
+static void client_indicator_update_geometry(struct client_indicator *ci)
+{
+       if(ci->orientation == CI_HORIZONTAL) {
+               ci->geom.x = ci->monitor->geom.x;
+               ci->geom.y = ci->monitor->geom.y + bar_height;
+               ci->geom.w = ci->monitor->geom.w;
+               ci->geom.h = clind_height;
+       } else {
+               ci->geom.x = ci->monitor->geom.x + ci->monitor->geom.w - clind_height;
+               ci->geom.y = ci->monitor->geom.y + bar_height;
+               ci->geom.w = clind_height;
+               ci->geom.h = ci->monitor->geom.h - bar_height;
+       }
+
+       return;
+}
+
+static struct client_indicator* client_indicator_new(struct monitor *monitor, int orientation)
+{
+       struct client_indicator *ci;
+       unsigned long mask;
+       XSetWindowAttributes attrs;
+       int depth;
+       Visual *visual;
+
+       memset(&attrs, 0, sizeof(attrs));
+
+       attrs.override_redirect = True;
+       attrs.background_pixmap = ParentRelative;
+       attrs.event_mask = ButtonPressMask | ExposureMask;
+       mask = CWOverrideRedirect | CWBackPixmap | CWEventMask;
+       depth = DefaultDepth(dpy, screen);
+       visual = DefaultVisual(dpy, screen);
+
+       ci = malloc(sizeof(*ci));
+
+       if(ci) {
+               memset(ci, 0, sizeof(*ci));
+
+               ci->orientation = orientation;
+               ci->monitor = monitor;
+
+               client_indicator_update_geometry(ci);
+
+               printf("XCreateWindow(dpy, rppt, %d, %d, %d, %d, ...)\n",
+                      ci->geom.x, ci->geom.y, ci->geom.w, ci->geom.h);
+               ci->win = XCreateWindow(dpy, root,
+                                       ci->geom.x, ci->geom.y, ci->geom.w, ci->geom.h, 0,
+                                       depth, CopyFromParent, visual,
+                                       mask, &attrs);
+               XSync(dpy, False);
+
+               ci->gc = XCreateGC(dpy, root, 0, NULL);
+               XSetLineAttributes(dpy, ci->gc, 1, LineSolid, CapButt, JoinMiter);
+
+               XDefineCursor(dpy, ci->win, cursor[CurNormal]);
+       }
+
+       return(ci);
+}
+
+static void client_indicator_free(struct client_indicator **indicator)
+{
+       if(!indicator || !*indicator) {
+               return;
+       }
+
+       XUnmapWindow(dpy, (*indicator)->win);
+       XDestroyWindow(dpy, (*indicator)->win);
+
+       free(*indicator);
+       *indicator = NULL;
+
+       return;
+}
+
+static void client_indicator_set_visible(struct client_indicator *ci, int visible)
+{
+#ifdef DEBUG_D
+       printf("client_indicator_set_visible(%s, %d)\n",
+              ci == ci->monitor->v_clind ? "v_clind" : "h_clind", visible);
+#endif
+
+       if(ci) {
+               if(visible) {
+                       XMapRaised(dpy, ci->win);
+               } else {
+                       XUnmapWindow(dpy, ci->win);
+               }
+       }
+
+       return;
+}
+
+static void client_indicator_resize(struct client_indicator *ci)
+{
+       ci->geom.x = ci->monitor->geom.x;
+       ci->geom.y = ci->monitor->geom.y + bar_height;
+       ci->geom.w = ci->monitor->geom.w;
+       ci->geom.h = clind_height;
+
+       XMoveResizeWindow(dpy, ci->win, ci->geom.x, ci->geom.y, ci->geom.w, ci->geom.h);
+       return;
+}
+
 static int rule_match(struct rule const *rule, XClassHint *class_hint, const char *client_name)
 {
        const char *class, *instance;
@@ -635,11 +801,11 @@ void workspace_update(struct workspace *workspace)
 void monitor_arrange_clients(struct monitor *monitor)
 {
        strncpy(monitor->ltsymbol,
-               monitor->lt[monitor->sellt]->symbol,
+               monitor->layout->symbol,
                sizeof(monitor->ltsymbol));
 
        if(!monitor_is_floating(monitor)) {
-               monitor->lt[monitor->sellt]->arrange(monitor);
+               monitor->layout->arrange(monitor);
        }
 
        restack(monitor);
@@ -686,7 +852,7 @@ void buttonpress(XEvent *event)
                monitor_focus(event_monitor);
        }
 
-       if(ev->window == selmon->barwin) {
+       if(ev->window == selmon->statusbar) {
                i = 0;
                x = 0;
 
@@ -749,7 +915,7 @@ void cleanup(void)
        foo.arrange = NULL;
 
        show_workspace(&a);
-       selmon->lt[selmon->sellt] = &foo;
+       selmon->layout = &foo;
 
        for(i = 0; i < LENGTH(workspaces); i++) {
                while(workspaces[i].clients) {
@@ -771,6 +937,7 @@ void cleanup(void)
        }
 
        XftDrawDestroy(dc.draw);
+
        g_object_unref(dc.font.layout);
        XFreeGC(dpy, dc.gc);
        XFreeCursor(dpy, cursor[CurNormal]);
@@ -798,8 +965,11 @@ static void monitor_free(struct monitor *mon)
                }
        }
 
-       XUnmapWindow(dpy, mon->barwin);
-       XDestroyWindow(dpy, mon->barwin);
+       client_indicator_free(&(mon->v_clind));
+       client_indicator_free(&(mon->h_clind));
+
+       XUnmapWindow(dpy, mon->statusbar);
+       XDestroyWindow(dpy, mon->statusbar);
        free(mon);
        num_monitors--;
 
@@ -880,27 +1050,36 @@ void client_configure(struct client *c)
 
 void configurenotify(XEvent *e)
 {
-        struct monitor *m;
        XConfigureEvent *ev = &e->xconfigure;
        Bool dirty;
 
        if(ev->window == root) {
-               dirty = (screen_width != ev->width);
+               dirty = screen_width != ev->width ||
+                       screen_height != ev->height;
                screen_width = ev->width;
                screen_height = ev->height;
 
                if(updategeom() || dirty) {
+                       struct monitor *mon;
+
                        if(dc.drawable != 0) {
                                XFreePixmap(dpy, dc.drawable);
                        }
 
                        dc.drawable = XCreatePixmap(dpy, root, screen_width, bar_height,
                                                    DefaultDepth(dpy, screen));
+
                        XftDrawChange(dc.draw, dc.drawable);
+
                        updatebars();
 
-                       for(m = mons; m; m = m->next) {
-                               monitor_resize_barwin(m);
+                       for(mon = mons; mon; mon = mon->next) {
+                               monitor_resize_statusbar(mon);
+
+                               if(mon->v_clind && mon->h_clind) {
+                                       client_indicator_resize(mon->v_clind);
+                                       client_indicator_resize(mon->h_clind);
+                               }
                        }
 
                        focus(NULL);
@@ -986,21 +1165,29 @@ void configurerequest(XEvent *e)
        return;
 }
 
-struct monitor *createmon(void)
+static struct monitor *monitor_new(int num, int x, int y, int w, int h)
 {
         struct monitor *m;
        int i;
 
-       m = (struct monitor *)calloc(1, sizeof(*m));
+       m = (struct monitor*)calloc(1, sizeof(*m));
 
        if(!m) {
                die("fatal: could not malloc() %u bytes\n", sizeof(*m));
        }
 
-       m->tagset[0] = 1;
-       m->tagset[1] = 1;
-       m->lt[0] = &layouts[0];
-       m->lt[1] = &layouts[1 % LENGTH(layouts)];
+       m->layout = &layouts[0];
+       strncpy(m->ltsymbol, layouts[0].symbol, sizeof(m->ltsymbol));
+
+       monitor_update_geometry(m, x, y, w, h);
+       m->num = num;
+
+       m->h_clind = client_indicator_new(m, CI_HORIZONTAL);
+       m->v_clind = client_indicator_new(m, CI_VERTICAL);
+
+       if(!m->h_clind || !m->v_clind) {
+               die("fatal: could not allocate memory\n");
+       }
 
        for(i = 0; i < LENGTH(workspaces); i++) {
                if(!workspaces[i].viewer) {
@@ -1011,12 +1198,28 @@ struct monitor *createmon(void)
                }
        }
 
-       strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+       if(!selmon) {
+               selmon = m;
+       }
+
        num_monitors++;
 
        return(m);
 }
 
+static struct monitor *monitor_get(int num)
+{
+       struct monitor *mon;
+
+       for(mon = mons; mon; mon = mon->next) {
+               if(mon->num == num) {
+                       break;
+               }
+       }
+
+       return(mon);
+}
+
 void destroynotify(XEvent *e)
 {
         struct client *c;
@@ -1117,7 +1320,7 @@ void drawbar(struct monitor *m)
        unsigned int i;
        int x;
 
-       monitor_resize_barwin(m);
+       monitor_resize_statusbar(m);
        wipe_bar(m, m == selmon ? PalSelected : PalNormal);
 
        focused = selmon->workspace->focused;
@@ -1164,7 +1367,7 @@ void drawbar(struct monitor *m)
 
        drawtext(stext, PalNormal, False);
 
-       XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0,
+       XCopyArea(dpy, dc.drawable, m->statusbar, dc.gc, 0, 0,
                  m->win_geom.w, bar_height, 0, 0);
        XSync(dpy, False);
 
@@ -1304,12 +1507,15 @@ void focus(struct client *c)
 
                grabbuttons(c, True);
                XSetWindowBorder(dpy, c->win, dc.palette[PalSelected].color[ColBorder]);
+
                setfocus(c);
        } else {
                XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
        }
 
        selmon->workspace->focused = c;
+       client_indicator_update(selmon->h_clind);
+       client_indicator_update(selmon->v_clind);
        drawbars();
 
        return;
@@ -1418,7 +1624,7 @@ Atom getatomprop(struct client *c, Atom prop)
                req = xatom[XembedInfo];
        }
 
-       if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
+       if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof(atom), False, req,
                              &da, &di, &dl, &dl, &p) == Success && p) {
                atom = *(Atom *)p;
 
@@ -1591,23 +1797,6 @@ void initfont(const char *fontstr)
        return;
 }
 
-#ifdef XINERAMA
-static Bool isuniquegeom(XineramaScreenInfo *unique, size_t n,
-                        XineramaScreenInfo *info)
-{
-       while(n--) {
-               if(unique[n].x_org == info->x_org &&
-                  unique[n].y_org == info->y_org &&
-                  unique[n].width == info->width &&
-                  unique[n].height == info->height) {
-                       return(False);
-               }
-       }
-
-       return(True);
-}
-#endif /* XINERAMA */
-
 void keypress(XEvent *e)
 {
        unsigned int i;
@@ -1692,7 +1881,7 @@ void manage(Window w, XWindowAttributes *wa)
        c->geom.x = MAX(c->geom.x, mon->geom.x);
 
        /* only fix client y-offset, if the client center might cover the bar */
-       c->geom.y = MAX(c->geom.y, (mon->by == mon->geom.y &&
+       c->geom.y = MAX(c->geom.y, (mon->win_geom.y == mon->geom.y &&
                                    (c->geom.x + (c->geom.w / 2) >= mon->win_geom.x) &&
                                    (c->geom.x + (c->geom.w / 2) < mon->win_geom.x +
                                     mon->win_geom.w)) ? bar_height : mon->geom.y);
@@ -1779,7 +1968,7 @@ void monocle(struct monitor *m)
        }
 
        if(n > 0) { /* override layout symbol */
-               snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+               snprintf(m->ltsymbol, sizeof(m->ltsymbol), "[%d]", n);
        }
 
        for(c = nexttiled(m->workspace->clients); c; c = nexttiled(c->next)) {
@@ -2007,9 +2196,9 @@ void client_resize_hints(struct client *c, int x, int y, int w, int h, Bool inte
        return;
 }
 
-void monitor_resize_barwin(struct monitor *m)
+void monitor_resize_statusbar(struct monitor *m)
 {
-       XMoveResizeWindow(dpy, m->barwin, m->win_geom.x, m->by, m->win_geom.w, bar_height);
+       XMoveResizeWindow(dpy, m->statusbar, m->geom.x, m->geom.y, m->geom.w, bar_height);
 
        return;
 }
@@ -2136,7 +2325,7 @@ void restack(struct monitor *monitor)
                struct client *client;
 
                wc.stack_mode = Below;
-               wc.sibling = monitor->barwin;
+               wc.sibling = monitor->statusbar;
 
                for(client = monitor->workspace->clients; client; client = client->next) {
                        if(!client->isfloating && ISVISIBLE(client)) {
@@ -2455,17 +2644,28 @@ void setfullscreen(struct client *client, Bool fullscreen)
        return;
 }
 
-void setlayout(const union arg *arg)
+static const struct layout *layout_next(const struct layout *current)
 {
-       if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
-               selmon->sellt ^= 1;
+        const struct layout *next;
+
+       next = current + 1;
+
+       if(next < &layouts[LENGTH(layouts) - 1]) {
+               return(next);
        }
 
+       return(&layouts[0]);
+}
+
+void setlayout(const union arg *arg)
+{
        if(arg && arg->v) {
-               selmon->lt[selmon->sellt] = (struct layout*)arg->v;
+               selmon->layout = (struct layout*)arg->v;
+       } else {
+               selmon->layout = layout_next(selmon->layout);
        }
 
-       strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol,
+       strncpy(selmon->ltsymbol, selmon->layout->symbol,
                sizeof(selmon->ltsymbol));
 
        if(selmon->workspace->focused) {
@@ -2474,6 +2674,9 @@ void setlayout(const union arg *arg)
                drawbar(selmon);
        }
 
+       client_indicator_update(selmon->v_clind);
+       client_indicator_update(selmon->h_clind);
+
        return;
 }
 
@@ -2504,10 +2707,11 @@ static void setup(void)
        screen = DefaultScreen(dpy);
        root = RootWindow(dpy, screen);
        initfont(font);
-       screen_width = DisplayWidth(dpy, screen);
-       screen_height = DisplayHeight(dpy, screen);
-       bar_height = dc.geom.h = dc.font.height + 2 * bar_padding;
-       updategeom();
+
+       /* init appearance */
+       for(i = 0; i < PalLast; i++) {
+               palette_init(&dc.palette[i], &theme[i]);
+       }
 
        /* init atoms */
        wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
@@ -2537,17 +2741,20 @@ static void setup(void)
        cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
        cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
 
-       /* init appearance */
-       for(i = 0; i < PalLast; i++) {
-               palette_init(&dc.palette[i], &theme[i]);
-       }
+       dc.gc = XCreateGC(dpy, root, 0, NULL);
+       XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
+
+       screen_width = DisplayWidth(dpy, screen);
+       screen_height = DisplayHeight(dpy, screen);
+       bar_height = dc.geom.h = dc.font.height + 2 * bar_padding;
 
        dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen),
                                    bar_height, DefaultDepth(dpy, screen));
+
        dc.draw = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen),
-                                       DefaultColormap(dpy, screen));
-       dc.gc = XCreateGC(dpy, root, 0, NULL);
-       XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
+                               DefaultColormap(dpy, screen));
+
+       updategeom();
 
        /* init bars */
        updatebars();
@@ -2570,7 +2777,7 @@ static void setup(void)
 
 static int monitor_is_floating(struct monitor *mon)
 {
-       if(mon->lt[mon->sellt]->arrange) {
+       if(mon->layout->arrange) {
                return(FALSE);
        }
 
@@ -2662,19 +2869,6 @@ static void move_focused(const union arg *arg)
        return;
 }
 
-#if 0
-static void tag(const union arg *arg)
-{
-       if(selmon->sel && arg->ui & TAGMASK) {
-               selmon->sel->tags = arg->ui & TAGMASK;
-               focus(NULL);
-               workspace_update(selmon->workspace);
-       }
-
-       return;
-}
-#endif
-
 static void tagmon(const union arg *arg)
 {
        if(selmon->workspace->focused && mons->next) {
@@ -2713,7 +2907,7 @@ static int count_tiled_clients(struct workspace *workspace)
 
 static void bookshelf(struct monitor *monitor)
 {
-        int n, w, x;
+        int n, w, x, y;
        int extraw;
        int hpad;
 
@@ -2726,6 +2920,10 @@ static void bookshelf(struct monitor *monitor)
                 return;
         }
 
+       /* map the horizontal window bar */
+       client_indicator_set_visible(monitor->h_clind, TRUE);
+       client_indicator_set_visible(monitor->v_clind, FALSE);
+
        /*
         * Start at x position m->wx, which is likely not going to be
         * 0 in a multi-head setup. In other words, m->wx and m->wy
@@ -2737,6 +2935,7 @@ static void bookshelf(struct monitor *monitor)
        hpad = (n + 1) * client_padding.h;
         w = (monitor->win_geom.w - hpad) / n;
         x = monitor->win_geom.x;
+       y = monitor->win_geom.y + clind_height + client_padding.v;
 
        /* make the first window a bit larger if the width can't be divided evenly */
        extraw = monitor->win_geom.w - hpad - (w * n);
@@ -2752,9 +2951,10 @@ static void bookshelf(struct monitor *monitor)
                client->inch = 0;
 
                width = w + extraw - border;
-               height = monitor->win_geom.h - border - 2 * client_padding.v;
+               height = monitor->win_geom.h - border -
+                       2 * client_padding.v - clind_height;
 
-                client_resize_hints(client, x + client_padding.h, monitor->win_geom.y + client_padding.v,
+                client_resize_hints(client, x + client_padding.h, y,
                                    width, height, False);
 
                /*
@@ -2787,6 +2987,10 @@ static void bookstack(struct monitor *monitor)
                 return;
         }
 
+       /* map the vertical window bar */
+       client_indicator_set_visible(monitor->h_clind, FALSE);
+       client_indicator_set_visible(monitor->v_clind, TRUE);
+
        vpad = (n + 1) * client_padding.v;
         h = (monitor->win_geom.h - vpad) / n;
         y = monitor->win_geom.y;
@@ -2801,7 +3005,8 @@ static void bookstack(struct monitor *monitor)
                client->incw = 0;
                client->inch = 0;
 
-               width = monitor->win_geom.w - border - 2 * client_padding.h;
+               width = monitor->win_geom.w - border -
+                       2 * client_padding.h - clind_height;
                height = h + extrah - border;
 
                 client_resize_hints(client, monitor->win_geom.x + client_padding.v, y + client_padding.h,
@@ -2849,6 +3054,11 @@ void unfocus(struct client *client, Bool setfocus)
                XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
        }
 
+       if(client->workspace->viewer) {
+               client_indicator_update(client->workspace->viewer->v_clind);
+               client_indicator_update(client->workspace->viewer->h_clind);
+       }
+
        return;
 }
 
@@ -2902,6 +3112,9 @@ void unmapnotify(XEvent *event)
 void updatebars(void)
 {
        struct monitor *monitor;
+       int depth;
+       Visual *visual;
+       unsigned long mask;
 
        XSetWindowAttributes attrs = {
                .override_redirect = True,
@@ -2909,16 +3122,24 @@ void updatebars(void)
                .event_mask = ButtonPressMask|ExposureMask
        };
 
+       depth = DefaultDepth(dpy, screen);
+       visual = DefaultVisual(dpy, screen);
+       mask = CWOverrideRedirect | CWBackPixmap | CWEventMask;
+
        for(monitor = mons; monitor; monitor = monitor->next) {
-               monitor->barwin = XCreateWindow(dpy, root, monitor->win_geom.x, monitor->by,
-                                               monitor->win_geom.w, bar_height, 0,
-                                               DefaultDepth(dpy, screen), CopyFromParent,
-                                               DefaultVisual(dpy, screen),
-                                               CWOverrideRedirect | CWBackPixmap | CWEventMask,
-                                               &attrs);
+               if(monitor->statusbar != 0) {
+                       XUnmapWindow(dpy, monitor->statusbar);
+                       XDestroyWindow(dpy, monitor->statusbar);
+               }
 
-               XDefineCursor(dpy, monitor->barwin, cursor[CurNormal]);
-               XMapRaised(dpy, monitor->barwin);
+               monitor->statusbar = XCreateWindow(dpy, root,
+                                                  monitor->geom.x, monitor->geom.y,
+                                                  monitor->geom.w, bar_height, 0,
+                                                  depth, CopyFromParent, visual,
+                                                  mask, &attrs);
+
+               XDefineCursor(dpy, monitor->statusbar, cursor[CurNormal]);
+               XMapRaised(dpy, monitor->statusbar);
        }
 
        return;
@@ -2929,90 +3150,101 @@ void updatebarpos(struct monitor *monitor)
        monitor->win_geom.y = monitor->geom.y;
        monitor->win_geom.h = monitor->geom.h;
        monitor->win_geom.h -= bar_height;
-       monitor->by = monitor->win_geom.y;
        monitor->win_geom.y = monitor->win_geom.y + bar_height;
 
        return;
 }
 
-Bool updategeom(void)
+static void monitor_attach(struct monitor *mon)
 {
-       Bool dirty = False;
+       struct monitor **last;
 
-       if(XineramaIsActive(dpy)) {
-               int i, j, n, nn;
-               XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
-               XineramaScreenInfo *unique = NULL;
+       for(last = &mons; *last; last = &((*last)->next));
+       *last = mon;
 
-               n = num_monitors;
+       return;
+}
 
-               /* only consider unique geometries as separate screens */
-               if(!(unique = (XineramaScreenInfo *)malloc(sizeof(*unique) * nn))) {
-                       die("fatal: could not malloc() %u bytes\n", sizeof(*unique) * nn);
-               }
+static void monitor_update_geometry(struct monitor *mon, int x, int y, int w, int h)
+{
+       mon->geom.x = x;
+       mon->geom.y = y;
+       mon->geom.w = w;
+       mon->geom.h = h;
 
-               for(i = 0, j = 0; i < nn; i++) {
-                       if(isuniquegeom(unique, j, &info[i])) {
-                               memcpy(&unique[j++], &info[i], sizeof(*unique));
-                       }
+       memcpy(&mon->win_geom, &mon->geom, sizeof(mon->win_geom));
+       updatebarpos(mon);
+
+       monitor_resize_statusbar(mon);
+
+       if(mon->v_clind && mon->h_clind) {
+               client_indicator_resize(mon->v_clind);
+               client_indicator_resize(mon->h_clind);
+       }
+
+       return;
+}
+
+static int have_xinerama_screen(int needle, XineramaScreenInfo *haystack, int num)
+{
+       int i;
+
+       for(i = 0; i < num; i++) {
+               if(haystack[i].screen_number == needle) {
+                       return(TRUE);
                }
+       }
 
-               XFree(info);
-               nn = j;
+       return(FALSE);
+}
 
-               if(n <= nn) {
-                       struct monitor *monitor;
+static int updategeom(void)
+{
+       int dirty = FALSE;
 
-                       for(i = 0; i < (nn - n); i++) { /* new monitors available */
-                               struct monitor **ptr;
+       if(XineramaIsActive(dpy)) {
+               XineramaScreenInfo *info;
+               struct monitor *monitor;
+               int num_mons;
+               int i;
 
-                               for(ptr = &mons; *ptr; ptr = &((*ptr)->next));
-                               *ptr = createmon();
-                       }
+               info = XineramaQueryScreens(dpy, &num_mons);
 
-                       for(i = 0, monitor = mons; i < nn && monitor; monitor = monitor->next, i++) {
-                               if(i >= n || (unique[i].x_org != monitor->geom.x ||
-                                             unique[i].y_org != monitor->geom.y ||
-                                             unique[i].width != monitor->geom.w ||
-                                             unique[i].height != monitor->geom.h)) {
-                                       dirty = True;
-                                       monitor->num = i;
-                                       monitor->geom.x = unique[i].x_org;
-                                       monitor->geom.y = unique[i].y_org;
-                                       monitor->geom.w = unique[i].width;
-                                       monitor->geom.h = unique[i].height;
-
-                                       memcpy(&(monitor->win_geom), &(monitor->geom),
-                                              sizeof(monitor->win_geom));
-
-                                       updatebarpos(monitor);
-                               }
-                       }
-               } else { /* less monitors available nn < n */
-                       struct monitor *monitor;
+               for(i = 0; i < num_mons; i++) {
+                       struct monitor *mon;
+
+                       mon = monitor_get(info->screen_number);
 
-                       for(i = nn; i < n; i++) {
-                               for(monitor = mons; monitor && monitor->next; monitor = monitor->next);
+                       if(mon) {
+                               monitor_update_geometry(mon, info[i].x_org, info[i].y_org,
+                                                       info[i].width, info[i].height);
+                               dirty = TRUE;
+                       } else {
+                               mon = monitor_new(info[i].screen_number,
+                                                 info[i].x_org, info[i].y_org,
+                                                 info[i].width, info[i].height);
 
+                               monitor_attach(mon);
+                       }
+               }
+
+               for(monitor = mons; monitor; monitor = monitor->next) {
+                       if(!have_xinerama_screen(monitor->num, info, num_mons)) {
                                if(monitor == selmon) {
-                                       selmon = mons;
+                                       selmon = dirtomon(1);
                                }
 
                                monitor_free(monitor);
                        }
                }
 
-               free(unique);
+               XFree(info);
        } else {
                if(!mons) {
-                       mons = createmon();
-               }
-
-               if(mons->geom.w != screen_width || mons->geom.h != screen_height) {
+                       mons = monitor_new(0, 0, 0, screen_width, screen_height);
+               } else if(mons->geom.w != screen_width || mons->geom.h != screen_height) {
+                       monitor_update_geometry(mons, 0, 0, screen_width, screen_height);
                        dirty = True;
-                       mons->geom.w = mons->win_geom.w = screen_width;
-                       mons->geom.h = mons->win_geom.h = screen_height;
-                       updatebarpos(mons);
                }
        }
 
@@ -3250,7 +3482,7 @@ struct monitor *wintomon(Window window)
        }
 
        for(monitor = mons; monitor; monitor = monitor->next) {
-               if(window == monitor->barwin) {
+               if(window == monitor->statusbar) {
                        return(monitor);
                }
        }