JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Name and search workspaces.
[spectrwm.git] / scrotwm.c
index 227c3f4..1575a43 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -1,4 +1,3 @@
-/* $scrotwm$ */
 /*
  * Copyright (c) 2009-2010-2011 Marco Peereboom <marco@peereboom.us>
  * Copyright (c) 2009-2010-2011 Ryan McBride <mcbride@countersiege.com>
  * DEALINGS IN THE SOFTWARE.
  */
 
-static const char      *cvstag =
-    "$scrotwm$";
-
-#define        SWM_VERSION     "0.9.33"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <err.h>
@@ -87,11 +81,20 @@ static const char   *cvstag =
 #include <X11/Xproto.h>
 #include <X11/Xutil.h>
 #include <X11/extensions/Xrandr.h>
+#include <X11/extensions/XTest.h>
 
 #ifdef __OSX__
 #include <osx.h>
 #endif
 
+#include "version.h"
+
+#ifdef SCROTWM_BUILDSTR
+static const char      *buildstr = SCROTWM_BUILDSTR;
+#else
+static const char      *buildstr = SCROTWM_VERSION;
+#endif
+
 #if RANDR_MAJOR < 1
 #  error XRandR versions less than 1.0 are not supported
 #endif
@@ -162,6 +165,9 @@ u_int32_t           swm_debug = 0
 #define SWM_FOCUS_SYNERGY      (1)
 #define SWM_FOCUS_FOLLOW       (2)
 
+#define SWM_CONF_DEFAULT       (0)
+#define SWM_CONF_KEYMAPPING    (1)
+
 #ifndef SWM_LIB
 #define SWM_LIB                        "/usr/local/lib/libswmhack.so"
 #endif
@@ -199,11 +205,24 @@ int                       select_list_pipe[2];
 int                    select_resp_pipe[2];
 pid_t                  searchpid;
 volatile sig_atomic_t  search_resp;
+int                    search_resp_action;
+
+/* search actions */
+enum {
+       SWM_SEARCH_NONE,
+       SWM_SEARCH_UNICONIFY,
+       SWM_SEARCH_NAME_WORKSPACE,
+       SWM_SEARCH_SEARCH_WORKSPACE
+};
 
 /* dialog windows */
 double                 dialog_ratio = .6;
 /* status bar */
-#define SWM_BAR_MAX    (256)
+#define SWM_BAR_MAX            (256)
+#define SWM_BAR_JUSTIFY_LEFT   (0)
+#define SWM_BAR_JUSTIFY_CENTER (1)
+#define SWM_BAR_JUSTIFY_RIGHT  (2)
+#define SWM_BAR_OFFSET         (4)
 char                   *bar_argv[] = { NULL, NULL };
 int                    bar_pipe[2];
 char                   bar_ext[SWM_BAR_MAX];
@@ -218,6 +237,7 @@ int                 bar_extra = 1;
 int                    bar_extra_running = 0;
 int                    bar_verbose = 1;
 int                    bar_height = 0;
+int                    bar_justify = SWM_BAR_JUSTIFY_LEFT;
 int                    stack_enabled = 1;
 int                    clock_enabled = 1;
 int                    urgent_enabled = 0;
@@ -236,6 +256,7 @@ int                 bar_fidx = 0;
 XFontStruct            *bar_fs;
 char                   *bar_fonts[] = { NULL, NULL, NULL, NULL };/* XXX Make fully dynamic */
 char                   *spawn_term[] = { NULL, NULL };         /* XXX Make fully dynamic */
+struct passwd          *pwd;
 
 #define SWM_MENU_FN    (2)
 #define SWM_MENU_NB    (4)
@@ -321,6 +342,8 @@ void        new_region(struct swm_screen *, int, int, int, int);
 void   unmanage_window(struct ws_win *);
 long   getstate(Window);
 
+int    conf_load(char *, int);
+
 struct layout {
        void            (*l_stack)(struct workspace *, struct swm_geometry *);
        void            (*l_config)(struct workspace *, int);
@@ -348,6 +371,7 @@ struct layout {
 /* define work spaces */
 struct workspace {
        int                     idx;            /* workspace index */
+       char                    *name;          /* workspace name */
        int                     always_raise;   /* raise windows on focus */
        struct layout           *cur_layout;    /* current layout handlers */
        struct ws_win           *focus;         /* may be NULL */
@@ -411,6 +435,8 @@ union arg {
 #define SWM_ARG_ID_CYCLEWS_DOWN        (41)
 #define SWM_ARG_ID_CYCLESC_UP  (42)
 #define SWM_ARG_ID_CYCLESC_DOWN        (43)
+#define SWM_ARG_ID_CYCLEWS_UP_ALL      (44)
+#define SWM_ARG_ID_CYCLEWS_DOWN_ALL    (45)
 #define SWM_ARG_ID_STACKINC    (50)
 #define SWM_ARG_ID_STACKDEC    (51)
 #define SWM_ARG_ID_SS_ALL      (60)
@@ -419,6 +445,14 @@ union arg {
 #define SWM_ARG_ID_CENTER      (71)
 #define SWM_ARG_ID_KILLWINDOW  (80)
 #define SWM_ARG_ID_DELETEWINDOW        (81)
+#define SWM_ARG_ID_WIDTHGROW   (90)
+#define SWM_ARG_ID_WIDTHSHRINK (91)
+#define SWM_ARG_ID_HEIGHTGROW  (92)
+#define SWM_ARG_ID_HEIGHTSHRINK        (93)
+#define SWM_ARG_ID_MOVEUP      (100)
+#define SWM_ARG_ID_MOVEDOWN    (101)
+#define SWM_ARG_ID_MOVELEFT    (102)
+#define SWM_ARG_ID_MOVERIGHT   (103)
        char                    **argv;
 };
 
@@ -613,12 +647,11 @@ void
 ewmh_autoquirk(struct ws_win *win)
 {
        int                     success, i;
-       unsigned long           *data = NULL;
-       unsigned long           n;
+       unsigned long           *data = NULL, n;
        Atom                    type;
 
        success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L),
-           XA_ATOM, &n, (unsigned char **)&data);
+           XA_ATOM, &n, (void *)&data);
 
        if (!success) {
                XFree(data);
@@ -807,7 +840,7 @@ ewmh_get_win_state(struct ws_win *win)
                win->ewmh_flags |= SWM_F_MANUAL;
 
        success = get_property(win->id, ewmh[_NET_WM_STATE].atom,
-           (~0L), XA_ATOM, &n, (unsigned char **)&states);
+           (~0L), XA_ATOM, &n, (void *)&states);
 
        if (!success)
                return;
@@ -1131,7 +1164,7 @@ setscreencolor(char *val, int i, int c)
 void
 fancy_stacker(struct workspace *ws)
 {
-       strcpy(ws->stacker, "[   ]");
+       strlcpy(ws->stacker, "[   ]", sizeof ws->stacker);
        if (ws->cur_layout->l_stack == vertical_stack)
                snprintf(ws->stacker, sizeof ws->stacker, "[%d|%d]",
                    ws->l_state.vertical_mwin, ws->l_state.vertical_stacks);
@@ -1143,11 +1176,11 @@ fancy_stacker(struct workspace *ws)
 void
 plain_stacker(struct workspace *ws)
 {
-       strcpy(ws->stacker, "[ ]");
+       strlcpy(ws->stacker, "[ ]", sizeof ws->stacker);
        if (ws->cur_layout->l_stack == vertical_stack)
-               strcpy(ws->stacker, "[|]");
+               strlcpy(ws->stacker, "[|]", sizeof ws->stacker);
        if (ws->cur_layout->l_stack == horizontal_stack)
-               strcpy(ws->stacker, "[-]");
+               strlcpy(ws->stacker, "[-]", sizeof ws->stacker);
 }
 
 void
@@ -1166,8 +1199,8 @@ custom_region(char *val)
        if (w < 1 || h < 1)
                errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
 
-       if (x  < 0 || x > DisplayWidth(display, sidx) ||
-           y < 0 || y > DisplayHeight(display, sidx) ||
+       if (x > DisplayWidth(display, sidx) ||
+           y > DisplayHeight(display, sidx) ||
            w + x > DisplayWidth(display, sidx) ||
            h + y > DisplayHeight(display, sidx)) {
                fprintf(stderr, "ignoring region %ux%u+%u+%u "
@@ -1195,10 +1228,31 @@ socket_setnonblock(int fd)
 void
 bar_print(struct swm_region *r, char *s)
 {
+       int                     textwidth, x;
+       size_t          len;
+
        XClearWindow(display, r->bar_window);
        XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
-       XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
-           strlen(s));
+
+       len = strlen(s);
+       textwidth = XTextWidth(bar_fs, s, len);
+
+       switch (bar_justify) {
+       case SWM_BAR_JUSTIFY_LEFT:
+               x = SWM_BAR_OFFSET;
+               break;
+       case SWM_BAR_JUSTIFY_CENTER:
+               x = (WIDTH(r) - textwidth) / 2;
+               break;
+       case SWM_BAR_JUSTIFY_RIGHT:
+               x = WIDTH(r) - textwidth - SWM_BAR_OFFSET;
+               break;
+       }
+
+       if (x < SWM_BAR_OFFSET)
+               x = SWM_BAR_OFFSET;
+
+       XDrawString(display, r->bar_window, bar_gc, x, bar_fs->ascent, s, len);
 }
 
 void
@@ -1262,18 +1316,21 @@ bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
        }
 }
 
+int            urgent[SWM_WS_MAX];
 void
 bar_urgent(char *s, ssize_t sz)
 {
        XWMHints                *wmh = NULL;
        struct ws_win           *win;
-       int                     i, j, got_some = 0;
-       char                    a[32], b[8];
+       int                     i, j;
+       char                    b[8];
 
        if (urgent_enabled == 0)
                return;
 
-       a[0] = '\0';
+       for (i = 0; i < SWM_WS_MAX; i++)
+               urgent[i] = 0;
+
        for (i = 0; i < ScreenCount(display); i++)
                for (j = 0; j < SWM_WS_MAX; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) {
@@ -1281,19 +1338,20 @@ bar_urgent(char *s, ssize_t sz)
                                if (wmh == NULL)
                                        continue;
 
-                               if (wmh->flags & XUrgencyHint) {
-                                       snprintf(b, sizeof b, "%d ", j + 1);
-                                       strlcat(a, b, sizeof a);
-                                       got_some = 1;
-                               }
+                               if (wmh->flags & XUrgencyHint)
+                                       urgent[j] = 1;
                                XFree(wmh);
                        }
 
-       if (got_some) {
-               strlcat(s, a, sz);
-               strlcat(s, "   ", sz);
-        } else
-               strlcat(s, "    ", sz);
+       strlcat(s, "* ", sz);
+       for (i = 0; i < SWM_WS_MAX; i++) {
+               if (urgent[i])
+                       snprintf(b, sizeof b, "%d ", i + 1);
+               else
+                       snprintf(b, sizeof b, "- ");
+               strlcat(s, b, sz);
+       }
+       strlcat(s, "*    ", sz);
 }
 
 void
@@ -1304,10 +1362,11 @@ bar_update(void)
        struct swm_region       *r;
        int                     i, x;
        size_t                  len;
+       char                    ws[SWM_BAR_MAX];
        char                    s[SWM_BAR_MAX];
        char                    cn[SWM_BAR_MAX];
        char                    loc[SWM_BAR_MAX];
-       char                    *b;
+       char                    *b, *stack = "";
 
        if (bar_enabled == 0)
                return;
@@ -1339,14 +1398,19 @@ bar_update(void)
                x = 1;
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
                        strlcpy(cn, "", sizeof cn);
+                       strlcpy(ws, "", sizeof ws);
                        if (r && r->ws) {
                                bar_urgent(cn, sizeof cn);
                                bar_class_name(cn, sizeof cn, r->ws->focus);
                                bar_window_name(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",
-                           x++, r->ws->idx + 1, r->ws->stacker, s, cn, bar_ext,
+                       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);
                }
@@ -1523,8 +1587,8 @@ version(struct swm_region *r, union arg *args)
 {
        bar_version = !bar_version;
        if (bar_version)
-               snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s",
-                   SWM_VERSION, cvstag);
+               snprintf(bar_vertext, sizeof bar_vertext, "Version: %s Build: %s",
+                   SCROTWM_VERSION, buildstr);
        else
                strlcpy(bar_vertext, "", sizeof bar_vertext);
        bar_update();
@@ -2098,6 +2162,7 @@ cyclews(struct swm_region *r, union arg *args)
 {
        union                   arg a;
        struct swm_screen       *s = r->s;
+       int                     cycle_all = 0;
 
        DNPRINTF(SWM_D_WS, "cyclews id %d "
            "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
@@ -2106,12 +2171,18 @@ cyclews(struct swm_region *r, union arg *args)
        a.id = r->ws->idx;
        do {
                switch (args->id) {
+               case SWM_ARG_ID_CYCLEWS_UP_ALL:
+                       cycle_all = 1;
+                       /* FALLTHROUGH */
                case SWM_ARG_ID_CYCLEWS_UP:
                        if (a.id < SWM_WS_MAX - 1)
                                a.id++;
                        else
                                a.id = 0;
                        break;
+               case SWM_ARG_ID_CYCLEWS_DOWN_ALL:
+                       cycle_all = 1;
+                       /* FALLTHROUGH */
                case SWM_ARG_ID_CYCLEWS_DOWN:
                        if (a.id > 0)
                                a.id--;
@@ -2122,7 +2193,8 @@ cyclews(struct swm_region *r, union arg *args)
                        return;
                };
 
-               if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))
+               if (!cycle_all &&
+                   (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist)))
                        continue;
                if (cycle_visible == 0 && s->ws[a.id].r != NULL)
                        continue;
@@ -3053,7 +3125,8 @@ send_to_ws(struct swm_region *r, union arg *args)
        /* Try to update the window's workspace property */
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
        if (ws_idx_atom &&
-           snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
+           snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", nws->idx) <
+               SWM_PROPLEN) {
                DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
                    ws_idx_str);
                XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
@@ -3064,6 +3137,13 @@ send_to_ws(struct swm_region *r, union arg *args)
 }
 
 void
+pressbutton(struct swm_region *r, union arg *args)
+{
+       XTestFakeButtonEvent(display, args->id, True, CurrentTime);
+       XTestFakeButtonEvent(display, args->id, False, CurrentTime);
+}
+
+void
 raise_toggle(struct swm_region *r, union arg *args)
 {
        if (r && r->ws == NULL)
@@ -3127,7 +3207,7 @@ uniconify(struct swm_region *r, union arg *args)
 {
        struct ws_win           *win;
        FILE                    *lfile;
-       char                    *name;
+       unsigned char           *name;
        int                     count = 0;
        unsigned long           len;
 
@@ -3148,8 +3228,9 @@ uniconify(struct swm_region *r, union arg *args)
                return;
 
        search_r = r;
+       search_resp_action = SWM_SEARCH_UNICONIFY;
 
-       spawn_select(r, args, "uniconify", &searchpid);
+       spawn_select(r, args, "search", &searchpid);
 
        if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
                return;
@@ -3171,35 +3252,71 @@ uniconify(struct swm_region *r, union arg *args)
        fclose(lfile);
 }
 
-#define MAX_RESP_LEN   1024
+void
+name_workspace(struct swm_region *r, union arg *args)
+{
+       struct workspace        *ws;
+       FILE                    *lfile;
+
+       DNPRINTF(SWM_D_MISC, "name_workspace\n");
+
+       if (r && r->ws)
+               ws = r->ws;
+       else
+               return;
+
+       search_r = r;
+       search_resp_action = SWM_SEARCH_NAME_WORKSPACE;
+
+       spawn_select(r, args, "name_workspace", &searchpid);
+
+       if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+               return;
+
+       fprintf(lfile, "%s", "");
+       fclose(lfile);
+}
 
 void
-search_do_resp(void)
+search_workspace(struct swm_region *r, union arg *args)
 {
-       ssize_t                 rbytes;
-       struct ws_win           *win;
-       char                    *name, *resp, *s;
-       unsigned long           len;
+       int                     i;
+       struct workspace        *ws;
+       FILE                    *lfile;
 
-       DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
+       DNPRINTF(SWM_D_MISC, "search_workspace\n");
 
-       search_resp = 0;
-       searchpid = 0;
+       if (r == NULL)
+               return;
 
-       if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
-               fprintf(stderr, "search: calloc\n");
+       search_r = r;
+       search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE;
+
+       spawn_select(r, args, "search", &searchpid);
+
+       if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
                return;
-       }
 
-       rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
-       if (rbytes <= 0) {
-               fprintf(stderr, "search: read error: %s\n", strerror(errno));
-               goto done;
+       for (i = 0; i < SWM_WS_MAX; i++) {
+               ws = &r->s->ws[i];
+               if (ws == NULL)
+                       continue;
+               fprintf(lfile, "%d%s%s\n", ws->idx + 1,
+                   (ws->name ? ":" : ""), (ws->name ? ws->name : ""));
        }
-       resp[rbytes] = '\0';
-       len = strlen(resp);
 
-       DNPRINTF(SWM_D_MISC, "search_do_resp: resp %s\n", resp);
+       fclose(lfile);
+}
+
+void
+search_resp_uniconify(char *resp, unsigned long len)
+{
+       unsigned char           *name;
+       struct ws_win           *win;
+       char                    *s;
+
+       DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp %s\n", resp);
+
        TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
                if (win->iconic == 0)
                        continue;
@@ -3219,7 +3336,109 @@ search_do_resp(void)
                }
                free(s);
        }
+}
+
+void
+search_resp_name_workspace(char *resp, unsigned long len)
+{
+       struct workspace        *ws;
+
+       DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp %s\n", resp);
+
+       if (search_r->ws == NULL)
+               return;
+       ws = search_r->ws;
+
+       if (ws->name) {
+               free(search_r->ws->name);
+               search_r->ws->name = NULL;
+       }
+
+       if (len > 1) {
+               ws->name = strdup(resp);
+               if (ws->name == NULL) {
+                       DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: strdup: %s",
+                           strerror(errno));
+                       return;
+               }
+               ws->name[len - 1] = '\0';
+       }
+}
+
+void
+search_resp_search_workspace(char *resp, unsigned long len)
+{
+       char                    *p, *q;
+       int                     ws_idx;
+       const char              *errstr;
+       union arg               a;
+
+       DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp %s\n", resp);
+
+       q = strdup(resp);
+       if (!q) {
+               DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s",
+                   strerror(errno));
+               return;
+       }
+       q[len - 1] = '\0';
+       p = strchr(q, ':');
+       if (p != NULL)
+               *p = '\0';
+       ws_idx = strtonum(q, 1, SWM_WS_MAX, &errstr);
+       if (errstr) {
+               DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
+                   errstr, q);
+               free(q);
+               return;
+       }
+       free(q);
+       a.id = ws_idx - 1;
+       switchws(search_r, &a);
+}
+
+#define MAX_RESP_LEN   1024
+
+void
+search_do_resp(void)
+{
+       ssize_t                 rbytes;
+       char                    *resp;
+       unsigned long           len;
+
+       DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
+
+       search_resp = 0;
+       searchpid = 0;
+
+       if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
+               fprintf(stderr, "search: calloc\n");
+               goto done;
+       }
+
+       rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
+       if (rbytes <= 0) {
+               fprintf(stderr, "search: read error: %s\n", strerror(errno));
+               goto done;
+       }
+       resp[rbytes] = '\0';
+       len = strlen(resp);
+
+       switch (search_resp_action) {
+       case SWM_SEARCH_UNICONIFY:
+               search_resp_uniconify(resp, len);
+               break;
+       case SWM_SEARCH_NAME_WORKSPACE:
+               search_resp_name_workspace(resp, len);
+               break;
+       case SWM_SEARCH_SEARCH_WORKSPACE:
+               search_resp_search_workspace(resp, len);
+               break;
+       }
+
 done:
+       search_resp_action = SWM_SEARCH_NONE;
+       close(select_resp_pipe[0]);
        free(resp);
 }
 
@@ -3325,14 +3544,20 @@ resize_window(struct ws_win *win, int center)
        XConfigureWindow(display, win->id, mask, &wc);
 }
 
+#define SWM_RESIZE_STEPS       (50)
+
 void
 resize(struct ws_win *win, union arg *args)
 {
        XEvent                  ev;
        Time                    time = 0;
-       struct swm_region       *r = win->ws->r;
+       struct swm_region       *r = NULL;
        int                     relx, rely;
+       int                     resize_step = 0;
 
+       if (win == NULL)
+               return;
+       r = win->ws->r;
 
        DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %lu\n",
            win->id, win->floating, win->transient);
@@ -3349,6 +3574,33 @@ resize(struct ws_win *win, union arg *args)
            _NET_WM_STATE_ADD);
 
        stack();
+
+       switch (args->id) {
+       case SWM_ARG_ID_WIDTHSHRINK:
+               win->g.w -= SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       case SWM_ARG_ID_WIDTHGROW:
+               win->g.w += SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       case SWM_ARG_ID_HEIGHTSHRINK:
+               win->g.h -= SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       case SWM_ARG_ID_HEIGHTGROW:
+               win->g.h += SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       default:
+               break;
+       }
+       if (resize_step) {
+               resize_window(win, 0);
+               store_float_geom(win,r);
+               return;
+       }
+
        if (focus_mode == SWM_FOCUS_DEFAULT)
                drain_enter_notify();
 
@@ -3416,6 +3668,20 @@ resize(struct ws_win *win, union arg *args)
 }
 
 void
+resize_step(struct swm_region *r, union arg *args)
+{
+       struct ws_win           *win = NULL;
+
+       if (r && r->ws && r->ws->focus)
+               win = r->ws->focus;
+       else
+               return;
+
+       resize(win, args);
+}
+
+
+void
 move_window(struct ws_win *win)
 {
        unsigned int            mask;
@@ -3435,12 +3701,19 @@ move_window(struct ws_win *win)
        XConfigureWindow(display, win->id, mask, &wc);
 }
 
+#define SWM_MOVE_STEPS (50)
+
 void
 move(struct ws_win *win, union arg *args)
 {
        XEvent                  ev;
        Time                    time = 0;
-       struct swm_region       *r = win->ws->r;
+       int                     move_step = 0;
+       struct swm_region       *r = NULL;
+
+       if (win == NULL)
+               return;
+       r = win->ws->r;
 
        DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %lu\n",
            win->id, win->floating, win->transient);
@@ -3459,6 +3732,34 @@ move(struct ws_win *win, union arg *args)
 
        stack();
 
+       move_step = 0;
+       switch (args->id) {
+       case SWM_ARG_ID_MOVELEFT:
+               win->g.x -= (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       case SWM_ARG_ID_MOVERIGHT:
+               win->g.x += (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       case SWM_ARG_ID_MOVEUP:
+               win->g.y -= (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       case SWM_ARG_ID_MOVEDOWN:
+               win->g.y += (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       default:
+               break;
+       }
+       if (move_step) {
+               move_window(win);
+               store_float_geom(win,r);
+               return;
+       }
+
+
        if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
            GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
                return;
@@ -3504,6 +3805,23 @@ move(struct ws_win *win, union arg *args)
        drain_enter_notify();
 }
 
+void
+move_step(struct swm_region *r, union arg *args)
+{
+       struct ws_win           *win = NULL;
+
+       if (r && r->ws && r->ws->focus)
+               win = r->ws->focus;
+       else
+               return;
+
+       if (!(win->transient != 0 || win->floating != 0))
+               return;
+
+       move(win, args);
+}
+
+
 /* user/key callable function IDs */
 enum keyfuncid {
        kf_cycle_layout,
@@ -3536,6 +3854,8 @@ enum keyfuncid {
        kf_ws_10,
        kf_ws_next,
        kf_ws_prev,
+       kf_ws_next_all,
+       kf_ws_prev_all,
        kf_ws_prior,
        kf_screen_next,
        kf_screen_prev,
@@ -3562,6 +3882,17 @@ enum keyfuncid {
        kf_iconify,
        kf_uniconify,
        kf_raise_toggle,
+       kf_button2,
+       kf_width_shrink,
+       kf_width_grow,
+       kf_height_shrink,
+       kf_height_grow,
+       kf_move_left,
+       kf_move_right,
+       kf_move_up,
+       kf_move_down,
+       kf_name_workspace,
+       kf_search_workspace,
        kf_dumpwins, /* MUST BE LAST */
        kf_invalid
 };
@@ -3613,6 +3944,8 @@ struct keyfunc {
        { "ws_10",              switchws,       {.id = 9} },
        { "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} },
+       { "ws_prev_all",        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} },
        { "ws_prior",           priorws,        {0} },
        { "screen_next",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} },
        { "screen_prev",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} },
@@ -3639,6 +3972,17 @@ struct keyfunc {
        { "iconify",            iconify,        {0} },
        { "uniconify",          uniconify,      {0} },
        { "raise_toggle",       raise_toggle,   {0} },
+       { "button2",            pressbutton,    {2} },
+       { "width_shrink",       resize_step,    {.id = SWM_ARG_ID_WIDTHSHRINK} },
+       { "width_grow",         resize_step,    {.id = SWM_ARG_ID_WIDTHGROW} },
+       { "height_shrink",      resize_step,    {.id = SWM_ARG_ID_HEIGHTSHRINK} },
+       { "height_grow",        resize_step,    {.id = SWM_ARG_ID_HEIGHTGROW} },
+       { "move_left",          move_step,      {.id = SWM_ARG_ID_MOVELEFT} },
+       { "move_right",         move_step,      {.id = SWM_ARG_ID_MOVERIGHT} },
+       { "move_up",            move_step,      {.id = SWM_ARG_ID_MOVEUP} },
+       { "move_down",          move_step,      {.id = SWM_ARG_ID_MOVEDOWN} },
+       { "name_workspace",     name_workspace, {0} },
+       { "search_workspace",   search_workspace,       {0} },
        { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
        { "invalid key func",   NULL,           {0} },
 };
@@ -3971,13 +4315,20 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
-       setconfspawn("uniconify",       "dmenu"
+       setconfspawn("search",          "dmenu"
                                        " -i"
                                        " -fn $bar_font"
                                        " -nb $bar_color"
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+       setconfspawn("name_workspace",  "dmenu"
+                                       " -p Workspace"
+                                       " -fn $bar_font"
+                                       " -nb $bar_color"
+                                       " -nf $bar_font_color"
+                                       " -sb $bar_border"
+                                       " -sf $bar_color",      0);
 }
 
 /* key bindings */
@@ -4188,7 +4539,7 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next,   NULL);
        setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev,   NULL);
        setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_term,  NULL);
-       setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,        "menu");
+       setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,"menu");
        setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit,        NULL);
        setkeybinding(MODKEY,           XK_q,           kf_restart,     NULL);
        setkeybinding(MODKEY,           XK_m,           kf_focus_main,  NULL);
@@ -4204,6 +4555,8 @@ setup_keys(void)
        setkeybinding(MODKEY,           XK_0,           kf_ws_10,       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);
+       setkeybinding(MODKEY,           XK_Down,        kf_ws_prev_all, NULL);
        setkeybinding(MODKEY,           XK_a,           kf_ws_prior,    NULL);
        setkeybinding(MODKEY|ShiftMask, XK_Right,       kf_screen_next, NULL);
        setkeybinding(MODKEY|ShiftMask, XK_Left,        kf_screen_prev, NULL);
@@ -4222,21 +4575,63 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev,  NULL);
        setkeybinding(MODKEY|ShiftMask, XK_x,           kf_wind_kill,   NULL);
        setkeybinding(MODKEY,           XK_x,           kf_wind_del,    NULL);
-       setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,        "screenshot_all");
-       setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,        "screenshot_wind");
+       setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,"screenshot_all");
+       setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,"screenshot_wind");
        setkeybinding(MODKEY,           XK_t,           kf_float_toggle,NULL);
        setkeybinding(MODKEY|ShiftMask, XK_v,           kf_version,     NULL);
-       setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,        "lock");
-       setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,        "initscr");
+       setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,"lock");
+       setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,"initscr");
        setkeybinding(MODKEY,           XK_w,           kf_iconify,     NULL);
        setkeybinding(MODKEY|ShiftMask, XK_w,           kf_uniconify,   NULL);
        setkeybinding(MODKEY|ShiftMask, XK_r,           kf_raise_toggle,NULL);
+       setkeybinding(MODKEY,           XK_v,           kf_button2,     NULL);
+       setkeybinding(MODKEY,           XK_equal,       kf_width_grow,  NULL);
+       setkeybinding(MODKEY,           XK_minus,       kf_width_shrink,NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_equal,       kf_height_grow, NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_minus,       kf_height_shrink,NULL);
+       setkeybinding(MODKEY,           XK_bracketleft, kf_move_left,   NULL);
+       setkeybinding(MODKEY,           XK_bracketright,kf_move_right,  NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down,   NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_slash,       kf_name_workspace,NULL);
+       setkeybinding(MODKEY,           XK_slash,       kf_search_workspace,NULL);
 #ifdef SWM_DEBUG
        setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
 #endif
 }
 
 void
+clear_keys(void)
+{
+       int                     i;
+
+       /* clear all key bindings, if any */
+       for (i = 0; i < keys_length; i++)
+               free(keys[i].spawn_name);
+       keys_length = 0;
+}
+
+int
+setkeymapping(char *selector, char *value, int flags)
+{
+       char                    keymapping_file[PATH_MAX];
+       DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n");
+       if (value[0] == '~')
+               snprintf(keymapping_file, sizeof keymapping_file, "%s/%s",
+                   pwd->pw_dir, &value[1]);
+       else
+               strlcpy(keymapping_file, value, sizeof keymapping_file);
+       clear_keys();
+       /* load new key bindings; if it fails, revert to default bindings */
+       if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) {
+               clear_keys();
+               setup_keys();
+       }
+       DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
+       return (0);
+}
+
+void
 updatenumlockmask(void)
 {
        unsigned int            i, j;
@@ -4473,13 +4868,14 @@ enum    { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
          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_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY
        };
 
 int
 setconfvalue(char *selector, char *value, int flags)
 {
-       int i;
+       int     i;
+
        switch (flags) {
        case SWM_S_BAR_DELAY:
                bar_delay = atoi(value);
@@ -4493,6 +4889,16 @@ setconfvalue(char *selector, char *value, int flags)
        case SWM_S_BAR_AT_BOTTOM:
                bar_at_bottom = atoi(value);
                break;
+       case SWM_S_BAR_JUSTIFY:
+               if (!strcmp(value, "left"))
+                       bar_justify = SWM_BAR_JUSTIFY_LEFT;
+               else if (!strcmp(value, "center"))
+                       bar_justify = SWM_BAR_JUSTIFY_CENTER;
+               else if (!strcmp(value, "right"))
+                       bar_justify = SWM_BAR_JUSTIFY_RIGHT;
+               else
+                       errx(1, "invalid bar_justify");
+               break;
        case SWM_S_STACK_ENABLED:
                stack_enabled = atoi(value);
                break;
@@ -4681,7 +5087,8 @@ setautorun(char *selector, char *value, int flags)
 int
 setlayout(char *selector, char *value, int flags)
 {
-       int                     ws_id, st, i, x, mg, ma, si, raise;
+       int                     ws_id, i, x, mg, ma, si, raise;
+       int                     st = SWM_V_STACK;
        char                    s[1024];
        struct workspace        *ws;
 
@@ -4759,6 +5166,8 @@ struct config_option configopt[] = {
        { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
        { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
        { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
+       { "bar_justify",                setconfvalue,   SWM_S_BAR_JUSTIFY },
+       { "keyboard_mapping",           setkeymapping,  0 },
        { "bind",                       setconfbinding, 0 },
        { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
        { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
@@ -4790,7 +5199,7 @@ struct config_option configopt[] = {
 
 
 int
-conf_load(char *filename)
+conf_load(char *filename, int keymapping)
 {
        FILE                    *config;
        char                    *line, *cp, *optsub, *optval;
@@ -4805,7 +5214,7 @@ conf_load(char *filename)
                return (1);
        }
        if ((config = fopen(filename, "r")) == NULL) {
-               warn("conf_load: fopen");
+               warn("conf_load: fopen: %s", filename);
                return (1);
        }
 
@@ -4845,6 +5254,11 @@ conf_load(char *filename)
                            filename, lineno, wordlen, cp);
                        return (1);
                }
+               if (keymapping && strcmp(opt->optname, "bind")) {
+                       warnx("%s: line %zd: invalid option %.*s",
+                           filename, lineno, wordlen, cp);
+                       return (1);
+               }
                cp += wordlen;
                cp += strspn(cp, " \t\n"); /* eat whitespace */
                /* get [selector] if any */
@@ -4973,7 +5387,7 @@ tryharder:
        if (prop == NULL)
                return (0);
 
-       ret = strtonum(prop, 0, UINT_MAX, &errstr);
+       ret = strtonum((const char *)prop, 0, UINT_MAX, &errstr);
        /* ignore error because strtonum returns 0 anyway */
        XFree(prop);
 
@@ -5067,7 +5481,7 @@ manage_window(Window id)
                p = NULL;
        } else if (prop && win->transient == 0) {
                DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
-               ws_idx = strtonum(prop, 0, 9, &errstr);
+               ws_idx = strtonum((const char *)prop, 0, 9, &errstr);
                if (errstr) {
                        DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
                            errstr, prop);
@@ -5108,7 +5522,8 @@ manage_window(Window id)
 
        /* Set window properties so we can remember this after reincarnation */
        if (ws_idx_atom && prop == NULL &&
-           snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
+           snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", ws->idx) <
+               SWM_PROPLEN) {
                DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
                    ws_idx_str);
                XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
@@ -5320,7 +5735,6 @@ buttonpress(XEvent *e)
 
        DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
 
-       action = root_click;
        if ((win = find_window(ev->window)) == NULL)
                return;
 
@@ -6050,6 +6464,7 @@ setup_screens(void)
                for (j = 0; j < SWM_WS_MAX; j++) {
                        ws = &screens[i].ws[j];
                        ws->idx = j;
+                       ws->name = NULL;
                        ws->focus = NULL;
                        ws->r = NULL;
                        ws->old_r = NULL;
@@ -6119,7 +6534,6 @@ workaround(void)
 int
 main(int argc, char *argv[])
 {
-       struct passwd           *pwd;
        struct swm_region       *r, *rr;
        struct ws_win           *winfocus = NULL;
        struct timeval          tv;
@@ -6132,9 +6546,10 @@ main(int argc, char *argv[])
        struct sigaction        sact;
 
        start_argv = argv;
-       fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
-           SWM_VERSION, cvstag);
-       if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+       fprintf(stderr, "Welcome to scrotwm V%s Build: %s\n",
+           SCROTWM_VERSION, buildstr);
+       if (!setlocale(LC_CTYPE, "") || !setlocale(LC_TIME, "") ||
+           !XSupportsLocale())
                warnx("no locale support");
 
        if (!(display = XOpenDisplay(0)))
@@ -6190,12 +6605,11 @@ main(int argc, char *argv[])
                                cfile = conf;
        }
        if (cfile)
-               conf_load(cfile);
+               conf_load(cfile, SWM_CONF_DEFAULT);
 
        setup_ewmh();
        /* set some values to work around bad programs */
        workaround();
-
        /* grab existing windows (before we build the bars) */
        grab_windows();