JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Missed a few spaces in the default format.
[spectrwm.git] / spectrwm.c
index 62dd195..2ecc60f 100644 (file)
@@ -88,6 +88,7 @@
 
 #include <X11/cursorfont.h>
 #include <X11/keysym.h>
+#include <X11/XKBlib.h>
 #include <X11/Xatom.h>
 #include <X11/Xlib.h>
 #include <X11/Xproto.h>
@@ -240,8 +241,7 @@ struct search_window {
        Window                          indicator;
 };
 TAILQ_HEAD(search_winlist, search_window);
-
-struct search_winlist search_wl;
+struct search_winlist                  search_wl;
 
 /* search actions */
 enum {
@@ -252,6 +252,11 @@ enum {
        SWM_SEARCH_SEARCH_WINDOW
 };
 
+#define SWM_STACK_TOP          (0)
+#define SWM_STACK_BOTTOM       (1)
+#define        SWM_STACK_ABOVE         (2)
+#define        SWM_STACK_BELOW         (3)
+
 /* dialog windows */
 double                 dialog_ratio = 0.6;
 /* status bar */
@@ -263,7 +268,8 @@ double                      dialog_ratio = 0.6;
 #define SWM_BAR_FONTS          "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*," \
                                "-*-profont-*-*-*-*-*-*-*-*-*-*-*-*,"       \
                                "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*,"    \
-                               "-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*"
+                               "-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*,"  \
+                               "-*-*-*-r-*--*-*-*-*-*-*-*-*"
 
 #ifdef X_HAVE_UTF8_STRING
 #define DRAWSTRING(x...)       Xutf8DrawString(x)
@@ -286,6 +292,7 @@ int                 bar_extra_running = 0;
 int                    bar_verbose = 1;
 int                    bar_height = 0;
 int                    bar_justify = SWM_BAR_JUSTIFY_LEFT;
+char                   *bar_format = NULL;
 int                    stack_enabled = 1;
 int                    clock_enabled = 1;
 int                    urgent_enabled = 0;
@@ -294,6 +301,10 @@ int                        title_name_enabled = 0;
 int                    title_class_enabled = 0;
 int                    window_name_enabled = 0;
 int                    focus_mode = SWM_FOCUS_DEFAULT;
+int                    focus_close = SWM_STACK_BELOW;
+int                    focus_close_wrap = 1;
+int                    focus_default = SWM_STACK_TOP;
+int                    spawn_position = SWM_STACK_TOP;
 int                    disable_border = 0;
 int                    border_width = 1;
 int                    verbose_layout = 0;
@@ -465,7 +476,6 @@ struct swm_screen {
        GC                      bar_gc;
 };
 struct swm_screen      *screens;
-int                    num_screens;
 
 /* args to functions */
 union arg {
@@ -597,11 +607,11 @@ get_property(Window id, Atom atom, long count, Atom type, unsigned long *nitems,
            &real, &format, nitems_ret, nbytes_ret, data);
 
        if (status != Success)
-               return False;
+               return (False);
        if (real != type)
-               return False;
+               return (False);
 
-       return True;
+       return (True);
 }
 
 void
@@ -669,7 +679,7 @@ setup_ewmh(void)
                for (j = 0; j < LENGTH(ewmh); j++)
                        XChangeProperty(display, screens[i].root,
                            sup_list, XA_ATOM, 32,
-                           PropModeAppend, (unsigned char *)&ewmh[j].atom,1);
+                           PropModeAppend, (unsigned char *)&ewmh[j].atom, 1);
        }
 }
 
@@ -752,10 +762,10 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs)
        struct swm_geometry     rg;
 
        if (!win->ws->r)
-               return 0;
+               return (0);
 
        if (!win->floating)
-               return 0;
+               return (0);
 
        DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: window: 0x%lx, "
            "fullscreen %s\n", win->id, YESNO(fs));
@@ -776,7 +786,7 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs)
                }
        }
 
-       return 1;
+       return (1);
 }
 
 void
@@ -1017,7 +1027,7 @@ geteventname(XEvent *e)
                name = "Unknown";
        }
 
-       return name;
+       return (name);
 }
 
 char *
@@ -1033,7 +1043,7 @@ xrandr_geteventname(XEvent *e)
                name = "Unknown";
        }
 
-       return name;
+       return (name);
 }
 
 void
@@ -1294,7 +1304,7 @@ socket_setnonblock(int fd)
 }
 
 void
-bar_print(struct swm_region *r, char *s)
+bar_print(struct swm_region *r, const char *s)
 {
        int                     x = 0;
        size_t                  len;
@@ -1341,69 +1351,49 @@ bar_extra_stop(void)
 }
 
 void
-bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus)
+bar_class_name(char *s, size_t sz, struct swm_region *r)
 {
-       int                     do_class, do_name;
-       Status                  status;
-       XClassHint              *xch = NULL;
+       if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
+               return;
+       if (r->ws->focus->ch.res_class != NULL)
+               strlcat(s, r->ws->focus->ch.res_class, sz);
+}
 
-       if ((title_name_enabled == 1 || title_class_enabled == 1) &&
-           cur_focus != NULL) {
-               if ((xch = XAllocClassHint()) == NULL)
-                       goto out;
-               status = XGetClassHint(display, cur_focus->id, xch);
-               if (status == BadWindow || status == BadAlloc)
-                       goto out;
-               do_class = (title_class_enabled && xch->res_class != NULL);
-               do_name = (title_name_enabled && xch->res_name != NULL);
-               if (do_class)
-                       strlcat(s, xch->res_class, sz);
-               if (do_class && do_name)
-                       strlcat(s, ":", sz);
-               if (do_name)
-                       strlcat(s, xch->res_name, sz);
-               strlcat(s, "    ", sz);
-       }
-out:
-       if (xch) {
-               XFree(xch->res_name);
-               XFree(xch->res_class);
-               XFree(xch);
-       }
+void
+bar_title_name(char *s, size_t sz, struct swm_region *r)
+{
+       if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
+               return;
+       if (r->ws->focus->ch.res_name != NULL)
+               strlcat(s, r->ws->focus->ch.res_name, sz);
 }
 
 void
-bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
+bar_window_name(char *s, size_t sz, struct swm_region *r)
 {
        unsigned char           *title;
 
-       if (window_name_enabled && cur_focus != NULL) {
-               title = get_win_name(cur_focus->id);
-               if (title != NULL) {
-                       DNPRINTF(SWM_D_BAR, "bar_window_name: title: %s\n",
-                           title);
-
-                       if (cur_focus->floating)
-                               strlcat(s, "(f) ", sz);
-                       strlcat(s, (char *)title, sz);
-                       strlcat(s, " ", sz);
-                       XFree(title);
-               }
-       }
+       if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
+               return;
+       if ((title = get_win_name(r->ws->focus->id)) == NULL)
+               return;
+
+       if (r->ws->focus->floating)
+               strlcat(s, "(f) ", sz);
+       strlcat(s, (char *)title, sz);
+
+       XFree(title);
 }
 
 int            urgent[SWM_WS_MAX];
 void
-bar_urgent(char *s, ssize_t sz)
+bar_urgent(char *s, size_t sz)
 {
        XWMHints                *wmh = NULL;
        struct ws_win           *win;
        int                     i, j;
        char                    b[8];
 
-       if (urgent_enabled == 0)
-               return;
-
        for (i = 0; i < workspace_limit; i++)
                urgent[i] = 0;
 
@@ -1419,7 +1409,6 @@ bar_urgent(char *s, ssize_t sz)
                                XFree(wmh);
                        }
 
-       strlcat(s, "* ", sz);
        for (i = 0; i < workspace_limit; i++) {
                if (urgent[i])
                        snprintf(b, sizeof b, "%d ", i + 1);
@@ -1427,22 +1416,237 @@ bar_urgent(char *s, ssize_t sz)
                        snprintf(b, sizeof b, "- ");
                strlcat(s, b, sz);
        }
-       strlcat(s, "*    ", sz);
 }
 
 void
-bar_update(void)
+bar_workspace_name(char *s, size_t sz, struct swm_region *r)
 {
-       time_t                  tmt;
+       if (r == NULL || r->ws == NULL)
+               return;
+       if (r->ws->name != NULL)
+               strlcat(s, r->ws->name, sz);
+}
+
+/* build the default bar format according to the defined enabled options */
+void
+bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
+{
+       /* if format provided, just copy the buffers */
+       if (bar_format != NULL) {
+               strlcpy(fmtnew, fmtexp, sz);
+               return;
+       }
+
+       /* reset the output buffer */
+       *fmtnew = '\0';
+
+       strlcat(fmtnew, "+N:+I ", sz);
+       if (stack_enabled)
+               strlcat(fmtnew, "+S", sz);
+       strlcat(fmtnew, " ", sz);
+
+       /* only show the workspace name if there's actually one */
+       if (r != NULL && r->ws != NULL && r->ws->name != NULL)
+               strlcat(fmtnew, "<+D>", sz);
+       strlcat(fmtnew, "   ", sz);
+
+       if (clock_enabled) {
+               strlcat(fmtnew, fmtexp, sz);
+               strlcat(fmtnew, "    ", sz);
+       }
+
+       /* bar_urgent already adds the space before the last asterisk */
+       if (urgent_enabled)
+               strlcat(fmtnew, "* +U*    ", sz);
+
+       if (title_class_enabled) {
+               strlcat(fmtnew, "+C", sz);
+               if (title_name_enabled == 0)
+                       strlcat(fmtnew, "    ", sz);
+       }
+
+       if (title_name_enabled) {
+               /* add a colon if showing the class and something is focused */
+               if (title_class_enabled && r != NULL && r->ws != NULL &&
+                   r->ws->focus != NULL)
+                       strlcat(fmtnew, ":", sz);
+               strlcat(fmtnew, "+T    ", sz);
+       }
+
+       if (window_name_enabled)
+               strlcat(fmtnew, "+64W ", sz);
+
+       /* finally add the action script output and the version */
+       strlcat(fmtnew, "    +A    +V", sz);
+}
+
+/* replaces the bar format character sequences (like in tmux(1)) */
+char *
+bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
+    size_t sz)
+{
+       char                    *ptr;
+       char                    num[8], tmp[SWM_BAR_MAX];
+       int                     limit;
+       size_t                  len, numoff = 0;
+
+       /* reset strlcat(3) buffer */
+       *tmp = '\0';
+
+       /* get number, if any */
+       fmt++;
+       while (*fmt != '\0' && isdigit((unsigned char) *fmt)) {
+               if (numoff >= sizeof num - 1)
+                       break;
+               num[numoff++] = *fmt++;
+       }
+       num[numoff] = '\0';
+
+       if ((limit = strtonum(num, 1, sizeof tmp - 1, NULL)) == 0)
+               limit = sizeof tmp - 1;
+
+       /* if number is too big, skip to the first non-digit */
+       if (numoff >= sizeof num - 1) {
+               while (*fmt != '\0' && isdigit((unsigned char) *fmt))
+                       fmt++;
+       }
+       /* there is nothing to replace (ie EOL) */
+       if (*fmt == '\0')
+               return (fmt);
+
+       switch (*fmt) {
+       case 'A':
+               snprintf(tmp, sizeof tmp, "%s", bar_ext);
+               break;
+       case 'C':
+               bar_class_name(tmp, sizeof tmp, r);
+               break;
+       case 'D':
+               bar_workspace_name(tmp, sizeof tmp, r);
+               break;
+       case 'I':
+               snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
+               break;
+       case 'N':
+               snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
+               break;
+       case 'S':
+               snprintf(tmp, sizeof tmp, "%s", r->ws->stacker);
+               break;
+       case 'T':
+               bar_title_name(tmp, sizeof tmp, r);
+               break;
+       case 'U':
+               bar_urgent(tmp, sizeof tmp);
+               break;
+       case 'V':
+               snprintf(tmp, sizeof tmp, "%s", bar_vertext);
+               break;
+       case 'W':
+               bar_window_name(tmp, sizeof tmp, r);
+               break;
+       default:
+               /* unknown character sequence; copy as-is */
+               snprintf(tmp, sizeof tmp, "+%c", *fmt);
+               break;
+       }
+
+       len = strlen(tmp);
+       ptr = tmp;
+       if (len < limit)
+               limit = len;
+       while (limit-- > 0) {
+               if (*offrep >= sz - 1)
+                       break;
+               fmtrep[(*offrep)++] = *ptr++;
+       }
+
+       fmt++;
+       return (fmt);
+}
+
+void
+bar_replace(char *fmt, char *fmtrep, struct swm_region *r, size_t sz)
+{
+       size_t                  off;
+
+       off = 0;
+       while (*fmt != '\0') {
+               if (*fmt != '+') {
+                       /* skip ordinary characters */
+                       if (off >= sz - 1)
+                               break;
+                       fmtrep[off++] = *fmt++;
+                       continue;
+               }
+
+               /* character sequence found; replace it */
+               fmt = bar_replace_seq(fmt, fmtrep, r, &off, sz);
+               if (off >= sz - 1)
+                       break;
+       }
+
+       fmtrep[off] = '\0';
+}
+
+void
+bar_fmt_expand(char *fmtexp, size_t sz)
+{
+       char                    *fmt = NULL;
+       size_t                  len;
        struct tm               tm;
+       time_t                  tmt;
+
+       /* start by grabbing the current time and date */
+       time(&tmt);
+       localtime_r(&tmt, &tm);
+
+       /* figure out what to expand */
+       if (bar_format != NULL)
+               fmt = bar_format;
+       else if (bar_format == NULL && clock_enabled)
+               fmt = clock_format;
+       /* if nothing to expand bail out */
+       if (fmt == NULL) {
+               *fmtexp = '\0';
+               return;
+       }
+
+       /* copy as-is, just in case the format shouldn't be expanded below */
+       strlcpy(fmtexp, fmt, sz);
+       /* finally pass the string through strftime(3) */
+#ifndef SWM_DENY_CLOCK_FORMAT
+       if ((len = strftime(fmtexp, sz, fmt, &tm)) == 0)
+               warnx("format too long");
+       fmtexp[len] = '\0';
+#endif
+}
+
+void
+bar_fmt_print(void)
+{
+       char                    fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX];
+       char                    fmtrep[SWM_BAR_MAX];
+       int                     i;
        struct swm_region       *r;
-       int                     i, x;
+
+       /* expand the format by first passing it through strftime(3) */
+       bar_fmt_expand(fmtexp, sizeof fmtexp);
+
+       for (i = 0; i < ScreenCount(display); i++) {
+               TAILQ_FOREACH(r, &screens[i].rl, entry) {
+                       bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
+                       bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
+                       bar_print(r, fmtrep);
+               }
+       }
+}
+
+void
+bar_update(void)
+{
        size_t                  len;
-       char                    ws[SWM_BAR_MAX];
-       char                    s[SWM_BAR_MAX];
-       unsigned char           cn[SWM_BAR_MAX];
-       char                    loc[SWM_BAR_MAX];
-       char                    *b, *stack = "";
+       char                    *b;
 
        if (bar_enabled == 0)
                return;
@@ -1460,52 +1664,11 @@ bar_update(void)
        } else
                strlcpy((char *)bar_ext, "", sizeof bar_ext);
 
-       if (clock_enabled == 0)
-               strlcpy(s, "", sizeof s);
-       else {
-               time(&tmt);
-               localtime_r(&tmt, &tm);
-               len = strftime(s, sizeof s, clock_format, &tm);
-               s[len] = '\0';
-               strlcat(s, "    ", sizeof s);
-       }
-
-       for (i = 0; i < ScreenCount(display); i++) {
-               x = 1;
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       strlcpy((char *)cn, "", sizeof cn);
-                       strlcpy(ws, "", sizeof ws);
-                       if (r && r->ws) {
-                               bar_urgent((char *)cn, sizeof cn);
-                               bar_class_name((char *)cn, sizeof cn,
-                                   r->ws->focus);
-                               bar_window_name((char *)cn, sizeof cn,
-                                   r->ws->focus);
-                               if (r->ws->name)
-                                       snprintf(ws, sizeof ws, "<%s>",
-                                           r->ws->name);
-                               if (stack_enabled)
-                                       stack = r->ws->stacker;
-
-                               snprintf(loc, sizeof loc, 
-                                   "%d:%d %s %s   %s%s    %s    %s",
-                                   x++, r->ws->idx + 1, stack, ws, s, cn,
-                                   bar_ext, bar_vertext);
-                               bar_print(r, loc);
-                       }
-               }
-       }
+       bar_fmt_print();
        alarm(bar_delay);
 }
 
 void
-bar_check_opts(void)
-{
-       if (title_class_enabled || title_name_enabled || window_name_enabled)
-               bar_update();
-}
-
-void
 bar_signal(int sig)
 {
        bar_alarm = 1;
@@ -2157,7 +2320,7 @@ unfocus_win(struct ws_win *win)
 
        XChangeProperty(display, win->s->root,
            ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
-           PropModeReplace, (unsigned char *)&none,1);
+           PropModeReplace, (unsigned char *)&none, 1);
 }
 
 void
@@ -2233,10 +2396,10 @@ focus_win(struct ws_win *win)
 
                XChangeProperty(display, win->s->root,
                    ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
-                   PropModeReplace, (unsigned char *)&win->id,1);
+                   PropModeReplace, (unsigned char *)&win->id, 1);
        }
 
-       bar_check_opts();
+       bar_update();
 }
 
 void
@@ -2288,8 +2451,6 @@ switchws(struct swm_region *r, union arg *args)
        a.id = SWM_ARG_ID_FOCUSCUR;
        focus(new_ws->r, &a);
 
-       bar_update();
-
        /* unmap old windows */
        if (unmap_old)
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
@@ -2474,7 +2635,6 @@ swapwin(struct swm_region *r, union arg *args)
                if (target == source) {
                        if (source->ws->focus_prev != NULL &&
                            source->ws->focus_prev != target)
-
                                source = source->ws->focus_prev;
                        else
                                return;
@@ -2509,8 +2669,6 @@ focus_prev(struct ws_win *win)
        struct ws_win_list      *wl = NULL;
        struct workspace        *ws = NULL;
 
-       DNPRINTF(SWM_D_FOCUS, "focus_prev: window: 0x%lx\n", WINID(win));
-
        if (!(win && win->ws))
                return;
 
@@ -2518,14 +2676,15 @@ focus_prev(struct ws_win *win)
        wl = &ws->winlist;
        cur_focus = ws->focus;
 
+       DNPRINTF(SWM_D_FOCUS, "focus_prev: window: 0x%lx, cur_focus: 0x%lx\n",
+           WINID(win), WINID(cur_focus));
+
        /* pickle, just focus on whatever */
        if (cur_focus == NULL) {
                /* use prev_focus if valid */
                if (ws->focus_prev && ws->focus_prev != cur_focus &&
                    find_window(WINID(ws->focus_prev)))
                        winfocus = ws->focus_prev;
-               if (winfocus == NULL)
-                       winfocus = TAILQ_FIRST(wl);
                goto done;
        }
 
@@ -2546,14 +2705,45 @@ focus_prev(struct ws_win *win)
                        goto done;
        }
 
-       if (cur_focus == win)
-               winfocus = TAILQ_PREV(win, ws_win_list, entry);
-       if (winfocus == NULL)
-               winfocus = TAILQ_LAST(wl, ws_win_list);
-       if (winfocus == NULL || winfocus == win)
-               winfocus = TAILQ_NEXT(cur_focus, entry);
+       DNPRINTF(SWM_D_FOCUS, "focus_prev: focus_close: %d\n", focus_close);
 
+       if (winfocus == NULL || winfocus == win) {
+               switch (focus_close) {
+               case SWM_STACK_BOTTOM:
+                       winfocus = TAILQ_FIRST(wl);
+                       break;
+               case SWM_STACK_TOP:
+                       winfocus = TAILQ_LAST(wl, ws_win_list);
+                       break;
+               case SWM_STACK_ABOVE:
+                       if ((winfocus = TAILQ_NEXT(cur_focus, entry)) == NULL) {
+                               if (focus_close_wrap)
+                                       winfocus = TAILQ_FIRST(wl);
+                               else
+                                       winfocus = TAILQ_PREV(cur_focus,
+                                           ws_win_list, entry);
+                       }
+                       break;
+               case SWM_STACK_BELOW:
+                       if ((winfocus = TAILQ_PREV(cur_focus, ws_win_list,
+                           entry)) == NULL) {
+                               if (focus_close_wrap)
+                                       winfocus = TAILQ_LAST(wl, ws_win_list);
+                               else
+                                       winfocus = TAILQ_NEXT(cur_focus, entry);
+                       }
+                       break;
+               }
+       }
 done:
+       if (winfocus == NULL) {
+               if (focus_default == SWM_STACK_TOP)
+                       winfocus = TAILQ_LAST(wl, ws_win_list);
+               else
+                       winfocus = TAILQ_FIRST(wl);
+       }
+
+       kill_refs(win);
        focus_magic(winfocus);
 }
 
@@ -2676,7 +2866,6 @@ cycle_layout(struct swm_region *r, union arg *args)
                drain_enter_notify();
        a.id = SWM_ARG_ID_FOCUSCUR;
        focus(r, &a);
-       bar_update();
 }
 
 void
@@ -3291,6 +3480,7 @@ send_to_ws(struct swm_region *r, union arg *args)
        }
 
        stack();
+       bar_update();
 }
 
 void
@@ -3737,16 +3927,16 @@ floating_toggle_win(struct ws_win *win)
        struct swm_region       *r;
 
        if (win == NULL)
-               return 0;
+               return (0);
 
        if (!win->ws->r)
-               return 0;
+               return (0);
 
        r = win->ws->r;
 
        /* reject floating toggles in max stack mode */
        if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
-               return 0;
+               return (0);
 
        if (win->floating) {
                if (!win->floatmaxed) {
@@ -3767,7 +3957,7 @@ floating_toggle_win(struct ws_win *win)
 
        ewmh_update_actions(win);
 
-       return 1;
+       return (1);
 }
 
 void
@@ -4384,7 +4574,7 @@ struct key {
        enum keyfuncid          funcid;
        char                    *spawn_name;
 };
-RB_HEAD(key_list, key);
+RB_HEAD(key_tree, key);
 
 int
 key_cmp(struct key *kp1, struct key *kp2)
@@ -4402,8 +4592,8 @@ key_cmp(struct key *kp1, struct key *kp2)
        return (0);
 }
 
-RB_GENERATE_STATIC(key_list, key, entry, key_cmp);
-struct key_list                        keys;
+RB_GENERATE(key_tree, key, entry, key_cmp);
+struct key_tree                        keys;
 
 /* mouse */
 enum { client_click, root_click };
@@ -4427,7 +4617,7 @@ update_modkey(unsigned int mod)
        struct key              *kp;
 
        mod_key = mod;
-       RB_FOREACH(kp, key_list, &keys)
+       RB_FOREACH(kp, key_tree, &keys)
                if (kp->mod & ShiftMask)
                        kp->mod = mod | ShiftMask;
                else
@@ -4795,7 +4985,7 @@ key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name)
        kp->keysym = ks;
        kp->funcid = kfid;
        kp->spawn_name = strdupsafe(spawn_name);
-       RB_INSERT(key_list, &keys, kp);
+       RB_INSERT(key_tree, &keys, kp);
 
        DNPRINTF(SWM_D_KEY, "key_insert: leave\n");
 }
@@ -4808,7 +4998,7 @@ key_lookup(unsigned int mod, KeySym ks)
        kp.keysym = ks;
        kp.mod = mod;
 
-       return (RB_FIND(key_list, &keys, &kp));
+       return (RB_FIND(key_tree, &keys, &kp));
 }
 
 void
@@ -4816,7 +5006,7 @@ key_remove(struct key *kp)
 {
        DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name);
 
-       RB_REMOVE(key_list, &keys, kp);
+       RB_REMOVE(key_tree, &keys, kp);
        free(kp->spawn_name);
        free(kp);
 
@@ -5079,7 +5269,7 @@ grabkeys(void)
                if (TAILQ_EMPTY(&screens[k].rl))
                        continue;
                XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
-               RB_FOREACH(kp, key_list, &keys) {
+               RB_FOREACH(kp, key_tree, &keys) {
                        if ((code = XKeysymToKeycode(display, kp->keysym)))
                                for (j = 0; j < LENGTH(modifiers); j++)
                                        XGrabKey(display, code,
@@ -5279,11 +5469,13 @@ enum    { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
          SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
          SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_WORKSPACE_LIMIT,
          SWM_S_SS_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
-         SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_URGENT_ENABLED,
-         SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH,
-         SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM,
-         SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM,
-         SWM_S_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY
+         SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED,
+         SWM_S_URGENT_ENABLED, SWM_S_FOCUS_MODE, SWM_S_FOCUS_CLOSE,
+         SWM_S_FOCUS_CLOSE_WRAP, SWM_S_FOCUS_DEFAULT, SWM_S_SPAWN_ORDER,
+         SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH, SWM_S_BAR_FONT,
+         SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO,
+         SWM_S_BAR_AT_BOTTOM, SWM_S_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY,
+         SWM_S_BAR_FORMAT
        };
 
 int
@@ -5315,6 +5507,11 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "invalid bar_justify");
                break;
+       case SWM_S_BAR_FORMAT:
+               free(bar_format);
+               if ((bar_format = strdup(value)) == NULL)
+                       err(1, "setconfvalue: bar_format");
+               break;
        case SWM_S_STACK_ENABLED:
                stack_enabled = atoi(value);
                break;
@@ -5369,6 +5566,41 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "focus_mode");
                break;
+       case SWM_S_FOCUS_CLOSE:
+               if (!strcmp(value, "first"))
+                       focus_close = SWM_STACK_BOTTOM;
+               else if (!strcmp(value, "last"))
+                       focus_close = SWM_STACK_TOP;
+               else if (!strcmp(value, "next"))
+                       focus_close = SWM_STACK_ABOVE;
+               else if (!strcmp(value, "previous"))
+                       focus_close = SWM_STACK_BELOW;
+               else
+                       errx(1, "focus_close");
+               break;
+       case SWM_S_FOCUS_CLOSE_WRAP:
+               focus_close_wrap = atoi(value);
+               break;
+       case SWM_S_FOCUS_DEFAULT:
+               if (!strcmp(value, "last"))
+                       focus_default = SWM_STACK_TOP;
+               else if (!strcmp(value, "first"))
+                       focus_default = SWM_STACK_BOTTOM;
+               else
+                       errx(1, "focus_default");
+               break;
+       case SWM_S_SPAWN_ORDER:
+               if (!strcmp(value, "first"))
+                       spawn_position = SWM_STACK_BOTTOM;
+               else if (!strcmp(value, "last"))
+                       spawn_position = SWM_STACK_TOP;
+               else if (!strcmp(value, "next"))
+                       spawn_position = SWM_STACK_ABOVE;
+               else if (!strcmp(value, "previous"))
+                       spawn_position = SWM_STACK_BELOW;
+               else
+                       errx(1, "spawn_position");
+               break;
        case SWM_S_DISABLE_BORDER:
                disable_border = atoi(value);
                break;
@@ -5593,6 +5825,7 @@ struct config_option configopt[] = {
        { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
        { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
        { "bar_justify",                setconfvalue,   SWM_S_BAR_JUSTIFY },
+       { "bar_format",                 setconfvalue,   SWM_S_BAR_FORMAT },
        { "keyboard_mapping",           setkeymapping,  0 },
        { "bind",                       setconfbinding, 0 },
        { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
@@ -5618,6 +5851,10 @@ struct config_option configopt[] = {
        { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
        { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
        { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
+       { "focus_close",                setconfvalue,   SWM_S_FOCUS_CLOSE },
+       { "focus_close_wrap",           setconfvalue,   SWM_S_FOCUS_CLOSE_WRAP },
+       { "focus_default",              setconfvalue,   SWM_S_FOCUS_DEFAULT },
+       { "spawn_position",             setconfvalue,   SWM_S_SPAWN_ORDER },
        { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
        { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
        { "autorun",                    setautorun,     0 },
@@ -5857,12 +6094,27 @@ manage_window(Window id)
                DNPRINTF(SWM_D_MISC, "manage_window: previously unmanaged "
                    "window: 0x%lx\n", win->id);
                TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
-               if (win->transient) {
+               if (win->transient)
                        set_child_transient(win, &trans);
-               } if (trans && (ww = find_window(trans)))
+
+               if (trans && (ww = find_window(trans)))
                        TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
-               else
+               else if ((ww = win->ws->focus) &&
+                   spawn_position == SWM_STACK_ABOVE)
+                       TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry);
+               else if (ww && spawn_position == SWM_STACK_BELOW)
+                       TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry);
+               else switch (spawn_position) {
+               default:
+               case SWM_STACK_TOP:
+               case SWM_STACK_ABOVE:
                        TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
+                       break;
+               case SWM_STACK_BOTTOM:
+               case SWM_STACK_BELOW:
+                       TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry);
+               }
+
                ewmh_update_actions(win);
                return (win);
        }
@@ -5949,6 +6201,8 @@ manage_window(Window id)
        win->s = r->s;  /* this never changes */
        if (trans && (ww = find_window(trans)))
                TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
+       else if (spawn_position == SWM_STACK_ABOVE && win->ws->focus)
+               TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry);
        else
                TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
 
@@ -6099,8 +6353,6 @@ unmanage_window(struct ws_win *win)
 
        TAILQ_REMOVE(&win->ws->winlist, win, entry);
        TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
-
-       kill_refs(win);
 }
 
 void
@@ -6110,7 +6362,7 @@ focus_magic(struct ws_win *win)
 
        if (win == NULL) {
                /* if there are no windows clear the status-bar */
-               bar_check_opts();
+               bar_update();
                return;
        }
 
@@ -6155,7 +6407,7 @@ keypress(XEvent *e)
        struct key              *kp;
        struct swm_region       *r;
 
-       keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
+       keysym = XkbKeycodeToKeysym(display, (KeyCode)ev->keycode, 0, 0);
        if ((kp = key_lookup(CLEANMASK(ev->state), keysym)) == NULL)
                return;
        if (keyfuncs[kp->funcid].func == NULL)
@@ -6213,9 +6465,8 @@ configurerequest(XEvent *e)
                wc.sibling = ev->above;
                wc.stack_mode = ev->detail;
                XConfigureWindow(display, ev->window, ev->value_mask, &wc);
-       } else {
+       } else
                config_win(win, ev);
-       }
 }
 
 void
@@ -6437,6 +6688,13 @@ mapnotify(XEvent *e)
        win = manage_window(ev->window);
        if (win)
                set_win_state(win, NormalState);
+
+       /*
+        * focus_win can only set input focus on a mapped window.
+        * make sure the window really has focus since it is just being mapped.
+        */
+       if (win->ws->focus == win)
+               focus_win(win);
 }
 
 void
@@ -6517,12 +6775,8 @@ propertynotify(XEvent *e)
                    X(win), Y(win), WIDTH(win), HEIGHT(win));
 #endif
        case XA_WM_CLASS:
-               if (title_name_enabled || title_class_enabled)
-                       bar_update();
-               break;
        case XA_WM_NAME:
-               if (window_name_enabled)
-                       bar_update();
+               bar_update();
                break;
        default:
                break;
@@ -6984,9 +7238,9 @@ workaround(void)
                    screens[i].c[SWM_S_COLOR_UNFOCUS].color);
 
                XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32,
-                   PropModeReplace, (unsigned char *)&win,1);
+                   PropModeReplace, (unsigned char *)&win, 1);
                XChangeProperty(display, win, netwmcheck, XA_WINDOW, 32,
-                   PropModeReplace, (unsigned char *)&win,1);
+                   PropModeReplace, (unsigned char *)&win, 1);
                XChangeProperty(display, win, netwmname, utf8_string, 8,
                    PropModeReplace, (unsigned char*)"LG3D", strlen("LG3D"));
        }