JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Reuse class hint in the status-bar.
[spectrwm.git] / spectrwm.c
index 02be1c5..796153b 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)
@@ -294,6 +300,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;
@@ -446,7 +456,9 @@ enum        { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
          SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
 
 /* physical screen mapping */
-#define SWM_WS_MAX             (10)
+#define SWM_WS_MAX             (22)    /* hard limit */
+int            workspace_limit = 10;   /* soft limit */
+
 struct swm_screen {
        int                     idx;    /* screen index */
        struct swm_region_list  rl;     /* list of regions on this screen */
@@ -463,7 +475,6 @@ struct swm_screen {
        GC                      bar_gc;
 };
 struct swm_screen      *screens;
-int                    num_screens;
 
 /* args to functions */
 union arg {
@@ -595,11 +606,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
@@ -667,7 +678,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);
        }
 }
 
@@ -750,10 +761,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));
@@ -774,7 +785,7 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs)
                }
        }
 
-       return 1;
+       return (1);
 }
 
 void
@@ -1015,7 +1026,7 @@ geteventname(XEvent *e)
                name = "Unknown";
        }
 
-       return name;
+       return (name);
 }
 
 char *
@@ -1031,7 +1042,7 @@ xrandr_geteventname(XEvent *e)
                name = "Unknown";
        }
 
-       return name;
+       return (name);
 }
 
 void
@@ -1342,32 +1353,24 @@ void
 bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus)
 {
        int                     do_class, do_name;
-       Status                  status;
-       XClassHint              *xch = NULL;
+       XClassHint              *ch;
 
-       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);
-       }
+       if (title_name_enabled == 0 && title_class_enabled == 0)
+               return;
+       if (cur_focus == NULL)
+               return;
+
+       ch = &cur_focus->ch;
+       do_class = (title_class_enabled && ch->res_class != NULL);
+       do_name = (title_name_enabled && ch->res_name != NULL);
+
+       if (do_class)
+               strlcat(s, ch->res_class, sz);
+       if (do_class && do_name)
+               strlcat(s, ":", sz);
+       if (do_name)
+               strlcat(s, ch->res_name, sz);
+       strlcat(s, "    ", sz);
 }
 
 void
@@ -1402,11 +1405,11 @@ bar_urgent(char *s, ssize_t sz)
        if (urgent_enabled == 0)
                return;
 
-       for (i = 0; i < SWM_WS_MAX; i++)
+       for (i = 0; i < workspace_limit; i++)
                urgent[i] = 0;
 
        for (i = 0; i < ScreenCount(display); i++)
-               for (j = 0; j < SWM_WS_MAX; j++)
+               for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) {
                                wmh = XGetWMHints(display, win->id);
                                if (wmh == NULL)
@@ -1418,7 +1421,7 @@ bar_urgent(char *s, ssize_t sz)
                        }
 
        strlcat(s, "* ", sz);
-       for (i = 0; i < SWM_WS_MAX; i++) {
+       for (i = 0; i < workspace_limit; i++) {
                if (urgent[i])
                        snprintf(b, sizeof b, "%d ", i + 1);
                else
@@ -1485,7 +1488,7 @@ bar_update(void)
                                if (stack_enabled)
                                        stack = r->ws->stacker;
 
-                               snprintf(loc, sizeof loc, 
+                               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);
@@ -1847,7 +1850,7 @@ unmap_all(void)
        int                     i, j;
 
        for (i = 0; i < ScreenCount(display); i++)
-               for (j = 0; j < SWM_WS_MAX; j++)
+               for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
                                unmap_window(win);
 }
@@ -1936,7 +1939,7 @@ find_unmanaged_window(Window id)
        int                     i, j;
 
        for (i = 0; i < ScreenCount(display); i++)
-               for (j = 0; j < SWM_WS_MAX; j++)
+               for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
                            entry)
                                if (id == win->id)
@@ -1953,7 +1956,7 @@ find_window(Window id)
        unsigned int            nc;
 
        for (i = 0; i < ScreenCount(display); i++)
-               for (j = 0; j < SWM_WS_MAX; j++)
+               for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
                                if (id == win->id)
                                        return (win);
@@ -1970,7 +1973,7 @@ find_window(Window id)
 
        /* look for parent */
        for (i = 0; i < ScreenCount(display); i++)
-               for (j = 0; j < SWM_WS_MAX; j++)
+               for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
                                if (wpr == win->id)
                                        return (win);
@@ -2058,7 +2061,7 @@ kill_refs(struct ws_win *win)
 
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       for (x = 0; x < SWM_WS_MAX; x++) {
+                       for (x = 0; x < workspace_limit; x++) {
                                ws = &r->s->ws[x];
                                if (win == ws->focus)
                                        ws->focus = NULL;
@@ -2080,7 +2083,7 @@ validate_win(struct ws_win *testwin)
 
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       for (x = 0; x < SWM_WS_MAX; x++) {
+                       for (x = 0; x < workspace_limit; x++) {
                                ws = &r->s->ws[x];
                                TAILQ_FOREACH(win, &ws->winlist, entry)
                                        if (win == testwin)
@@ -2099,7 +2102,7 @@ validate_ws(struct workspace *testws)
        /* validate all ws */
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       for (x = 0; x < SWM_WS_MAX; x++) {
+                       for (x = 0; x < workspace_limit; x++) {
                                ws = &r->s->ws[x];
                                if (ws == testws)
                                        return (0);
@@ -2155,7 +2158,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
@@ -2167,7 +2170,7 @@ unfocus_all(void)
        DNPRINTF(SWM_D_FOCUS, "unfocus_all\n");
 
        for (i = 0; i < ScreenCount(display); i++)
-               for (j = 0; j < SWM_WS_MAX; j++)
+               for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
                                unfocus_win(win);
 }
@@ -2231,7 +2234,7 @@ 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();
@@ -2249,6 +2252,9 @@ switchws(struct swm_region *r, union arg *args)
        if (!(r && r->s))
                return;
 
+       if (wsid >= workspace_limit)
+               return;
+
        this_r = r;
        old_ws = this_r->ws;
        new_ws = &this_r->s->ws[wsid];
@@ -2311,7 +2317,7 @@ cyclews(struct swm_region *r, union arg *args)
                        cycle_all = 1;
                        /* FALLTHROUGH */
                case SWM_ARG_ID_CYCLEWS_UP:
-                       if (a.id < SWM_WS_MAX - 1)
+                       if (a.id < workspace_limit - 1)
                                a.id++;
                        else
                                a.id = 0;
@@ -2323,7 +2329,7 @@ cyclews(struct swm_region *r, union arg *args)
                        if (a.id > 0)
                                a.id--;
                        else
-                               a.id = SWM_WS_MAX - 1;
+                               a.id = workspace_limit - 1;
                        break;
                default:
                        return;
@@ -2469,7 +2475,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;
@@ -2504,8 +2509,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;
 
@@ -2513,14 +2516,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;
        }
 
@@ -2541,14 +2545,44 @@ 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);
+       }
+
        focus_magic(winfocus);
 }
 
@@ -2918,9 +2952,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                if (w_inc > 1 && w_inc < v_slice) {
                        /* adjust for window's requested size increment */
                        remain = (win_g.w - w_base) % w_inc;
-                       missing = w_inc - remain;
                        win_g.w -= remain;
-                       extra += remain;
                }
 
                msize = win_g.w;
@@ -3241,6 +3273,9 @@ send_to_ws(struct swm_region *r, union arg *args)
        unsigned char           ws_idx_str[SWM_PROPLEN];
        union arg               a;
 
+       if (wsid >= workspace_limit)
+               return;
+
        if (r && r->ws && r->ws->focus)
                win = r->ws->focus;
        else
@@ -3297,7 +3332,7 @@ pressbutton(struct swm_region *r, union arg *args)
 void
 raise_toggle(struct swm_region *r, union arg *args)
 {
-       if (r && r->ws == NULL)
+       if (r == NULL || r->ws == NULL)
                return;
 
        r->ws->always_raise = !r->ws->always_raise;
@@ -3359,7 +3394,7 @@ uniconify(struct swm_region *r, union arg *args)
 
        DNPRINTF(SWM_D_MISC, "uniconify\n");
 
-       if (r && r->ws == NULL)
+       if (r == NULL || r->ws == NULL)
                return;
 
        /* make sure we have anything to uniconify */
@@ -3439,7 +3474,7 @@ search_workspace(struct swm_region *r, union arg *args)
        if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
                return;
 
-       for (i = 0; i < SWM_WS_MAX; i++) {
+       for (i = 0; i < workspace_limit; i++) {
                ws = &r->s->ws[i];
                if (ws == NULL)
                        continue;
@@ -3607,7 +3642,7 @@ search_resp_search_workspace(char *resp, unsigned long len)
        p = strchr(q, ':');
        if (p != NULL)
                *p = '\0';
-       ws_idx = strtonum(q, 1, SWM_WS_MAX, &errstr);
+       ws_idx = strtonum(q, 1, workspace_limit, &errstr);
        if (errstr) {
                DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
                    errstr, q);
@@ -3731,16 +3766,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) {
@@ -3761,7 +3796,7 @@ floating_toggle_win(struct ws_win *win)
 
        ewmh_update_actions(win);
 
-       return 1;
+       return (1);
 }
 
 void
@@ -4186,6 +4221,18 @@ enum keyfuncid {
        kf_ws_8,
        kf_ws_9,
        kf_ws_10,
+       kf_ws_11,
+       kf_ws_12,
+       kf_ws_13,
+       kf_ws_14,
+       kf_ws_15,
+       kf_ws_16,
+       kf_ws_17,
+       kf_ws_18,
+       kf_ws_19,
+       kf_ws_20,
+       kf_ws_21,
+       kf_ws_22,
        kf_ws_next,
        kf_ws_prev,
        kf_ws_next_all,
@@ -4203,6 +4250,18 @@ enum keyfuncid {
        kf_mvws_8,
        kf_mvws_9,
        kf_mvws_10,
+       kf_mvws_11,
+       kf_mvws_12,
+       kf_mvws_13,
+       kf_mvws_14,
+       kf_mvws_15,
+       kf_mvws_16,
+       kf_mvws_17,
+       kf_mvws_18,
+       kf_mvws_19,
+       kf_mvws_20,
+       kf_mvws_21,
+       kf_mvws_22,
        kf_bar_toggle,
        kf_wind_kill,
        kf_wind_del,
@@ -4278,6 +4337,18 @@ struct keyfunc {
        { "ws_8",               switchws,       {.id = 7} },
        { "ws_9",               switchws,       {.id = 8} },
        { "ws_10",              switchws,       {.id = 9} },
+       { "ws_11",              switchws,       {.id = 10} },
+       { "ws_12",              switchws,       {.id = 11} },
+       { "ws_13",              switchws,       {.id = 12} },
+       { "ws_14",              switchws,       {.id = 13} },
+       { "ws_15",              switchws,       {.id = 14} },
+       { "ws_16",              switchws,       {.id = 15} },
+       { "ws_17",              switchws,       {.id = 16} },
+       { "ws_18",              switchws,       {.id = 17} },
+       { "ws_19",              switchws,       {.id = 18} },
+       { "ws_20",              switchws,       {.id = 19} },
+       { "ws_21",              switchws,       {.id = 20} },
+       { "ws_22",              switchws,       {.id = 21} },
        { "ws_next",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} },
        { "ws_prev",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
        { "ws_next_all",        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} },
@@ -4295,6 +4366,18 @@ struct keyfunc {
        { "mvws_8",             send_to_ws,     {.id = 7} },
        { "mvws_9",             send_to_ws,     {.id = 8} },
        { "mvws_10",            send_to_ws,     {.id = 9} },
+       { "mvws_11",            send_to_ws,     {.id = 10} },
+       { "mvws_12",            send_to_ws,     {.id = 11} },
+       { "mvws_13",            send_to_ws,     {.id = 12} },
+       { "mvws_14",            send_to_ws,     {.id = 13} },
+       { "mvws_15",            send_to_ws,     {.id = 14} },
+       { "mvws_16",            send_to_ws,     {.id = 15} },
+       { "mvws_17",            send_to_ws,     {.id = 16} },
+       { "mvws_18",            send_to_ws,     {.id = 17} },
+       { "mvws_19",            send_to_ws,     {.id = 18} },
+       { "mvws_20",            send_to_ws,     {.id = 19} },
+       { "mvws_21",            send_to_ws,     {.id = 20} },
+       { "mvws_22",            send_to_ws,     {.id = 21} },
        { "bar_toggle",         bar_toggle,     {0} },
        { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
        { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
@@ -4888,6 +4971,18 @@ setup_keys(void)
        setkeybinding(MODKEY,           XK_8,           kf_ws_8,        NULL);
        setkeybinding(MODKEY,           XK_9,           kf_ws_9,        NULL);
        setkeybinding(MODKEY,           XK_0,           kf_ws_10,       NULL);
+       setkeybinding(MODKEY,           XK_F1,          kf_ws_11,       NULL);
+       setkeybinding(MODKEY,           XK_F2,          kf_ws_12,       NULL);
+       setkeybinding(MODKEY,           XK_F3,          kf_ws_13,       NULL);
+       setkeybinding(MODKEY,           XK_F4,          kf_ws_14,       NULL);
+       setkeybinding(MODKEY,           XK_F5,          kf_ws_15,       NULL);
+       setkeybinding(MODKEY,           XK_F6,          kf_ws_16,       NULL);
+       setkeybinding(MODKEY,           XK_F7,          kf_ws_17,       NULL);
+       setkeybinding(MODKEY,           XK_F8,          kf_ws_18,       NULL);
+       setkeybinding(MODKEY,           XK_F9,          kf_ws_19,       NULL);
+       setkeybinding(MODKEY,           XK_F10,         kf_ws_20,       NULL);
+       setkeybinding(MODKEY,           XK_F11,         kf_ws_21,       NULL);
+       setkeybinding(MODKEY,           XK_F12,         kf_ws_22,       NULL);
        setkeybinding(MODKEY,           XK_Right,       kf_ws_next,     NULL);
        setkeybinding(MODKEY,           XK_Left,        kf_ws_prev,     NULL);
        setkeybinding(MODKEY,           XK_Up,          kf_ws_next_all, NULL);
@@ -4905,6 +5000,18 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_8,           kf_mvws_8,      NULL);
        setkeybinding(MODKEY|ShiftMask, XK_9,           kf_mvws_9,      NULL);
        setkeybinding(MODKEY|ShiftMask, XK_0,           kf_mvws_10,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F1,          kf_mvws_11,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F2,          kf_mvws_12,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F3,          kf_mvws_13,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F4,          kf_mvws_14,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F5,          kf_mvws_15,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F6,          kf_mvws_16,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F7,          kf_mvws_17,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F8,          kf_mvws_18,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F9,          kf_mvws_19,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F10,         kf_mvws_20,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F11,         kf_mvws_21,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_F12,         kf_mvws_22,     NULL);
        setkeybinding(MODKEY,           XK_b,           kf_bar_toggle,  NULL);
        setkeybinding(MODKEY,           XK_Tab,         kf_focus_next,  NULL);
        setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev,  NULL);
@@ -5199,13 +5306,14 @@ setup_quirks(void)
 
 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_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_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_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
        };
 
 int
@@ -5256,6 +5364,13 @@ setconfvalue(char *selector, char *value, int flags)
        case SWM_S_CYCLE_VISIBLE:
                cycle_visible = atoi(value);
                break;
+       case SWM_S_WORKSPACE_LIMIT:
+               workspace_limit = atoi(value);
+               if (workspace_limit > SWM_WS_MAX)
+                       workspace_limit = SWM_WS_MAX;
+               else if (workspace_limit < 1)
+                       workspace_limit = 1;
+               break;
        case SWM_S_SS_ENABLED:
                ss_enabled = atoi(value);
                break;
@@ -5284,6 +5399,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;
@@ -5378,7 +5528,7 @@ setautorun(char *selector, char *value, int flags)
        if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
                errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'");
        ws_id--;
-       if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+       if (ws_id < 0 || ws_id >= workspace_limit)
                errx(1, "autorun: invalid workspace %d", ws_id + 1);
 
        /*
@@ -5443,7 +5593,7 @@ setlayout(char *selector, char *value, int flags)
                    "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
                    "<type>'");
        ws_id--;
-       if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+       if (ws_id < 0 || ws_id >= workspace_limit)
                errx(1, "layout: invalid workspace %d", ws_id + 1);
 
        if (!strcasecmp(s, "vertical"))
@@ -5517,6 +5667,7 @@ struct config_option configopt[] = {
        { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
        { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
        { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
+       { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
        { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
        { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
        { "modkey",                     setconfmodkey,  0 },
@@ -5532,6 +5683,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 },
@@ -5757,7 +5912,7 @@ manage_window(Window id)
        Atom                    *prot = NULL, *pp;
        unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
        struct swm_region       *r;
-       long                    mask;
+       long                    mask = 0;
        const char              *errstr;
        XWindowChanges          wc;
        struct pid_e            *p;
@@ -5771,12 +5926,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);
        }
@@ -5834,7 +6004,8 @@ manage_window(Window id)
                p = NULL;
        } else if (prop && win->transient == 0) {
                DNPRINTF(SWM_D_PROP, "manage_window: get _SWM_WS: %s\n", prop);
-               ws_idx = strtonum((const char *)prop, 0, 9, &errstr);
+               ws_idx = strtonum((const char *)prop, 0, workspace_limit - 1,
+                   &errstr);
                if (errstr) {
                        DNPRINTF(SWM_D_EVENT, "manage_window: window: #%s: %s",
                            errstr, prop);
@@ -5862,6 +6033,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);
 
@@ -5944,7 +6117,7 @@ manage_window(Window id)
        if (border_me) {
                bzero(&wc, sizeof wc);
                wc.border_width = border_width;
-               mask = CWBorderWidth;
+               mask |= CWBorderWidth;
                XConfigureWindow(display, win->id, mask, &wc);
        }
 
@@ -6068,7 +6241,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)
@@ -6126,9 +6299,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
@@ -6350,6 +6522,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
@@ -6636,7 +6815,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
 
        /* if we don't have a workspace already, find one */
        if (ws == NULL) {
-               for (i = 0; i < SWM_WS_MAX; i++)
+               for (i = 0; i < workspace_limit; i++)
                        if (s->ws[i].r == NULL) {
                                ws = &s->ws[i];
                                break;
@@ -6897,9 +7076,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"));
        }