JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Ignore window crossing events that are generated when the cursor is not actually...
[spectrwm.git] / scrotwm.c
index ac9b894..92145c4 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -1,10 +1,11 @@
 /*
- * Copyright (c) 2009-2010-2011 Marco Peereboom <marco@peereboom.us>
- * Copyright (c) 2009-2010-2011 Ryan McBride <mcbride@countersiege.com>
+ * Copyright (c) 2009-2012 Marco Peereboom <marco@peereboom.us>
+ * Copyright (c) 2009-2011 Ryan McBride <mcbride@countersiege.com>
  * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
  * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
  * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
+ * Copyright (c) 2011-2012 Reginald Kennedy <rk@rejii.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -178,6 +179,7 @@ Atom                        aprot;
 Atom                   adelete;
 Atom                   takefocus;
 Atom                   a_wmname;
+Atom                   a_netwmname;
 Atom                   a_utf8_string;
 Atom                   a_string;
 Atom                   a_swm_iconic;
@@ -207,10 +209,23 @@ pid_t                     searchpid;
 volatile sig_atomic_t  search_resp;
 int                    search_resp_action;
 
+struct search_window {
+       TAILQ_ENTRY(search_window)      entry;
+       int                                     idx;
+       struct ws_win           *win;
+       Window                          indicator;
+};
+TAILQ_HEAD(search_winlist, search_window);
+
+struct search_winlist search_wl;
+
 /* search actions */
 enum {
        SWM_SEARCH_NONE,
-       SWM_SEARCH_UNICONIFY
+       SWM_SEARCH_UNICONIFY,
+       SWM_SEARCH_NAME_WORKSPACE,
+       SWM_SEARCH_SEARCH_WORKSPACE,
+       SWM_SEARCH_SEARCH_WINDOW
 };
 
 /* dialog windows */
@@ -369,6 +384,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 */
@@ -1145,16 +1161,16 @@ setscreencolor(char *val, int i, int c)
                screens[i - 1].c[c].color = name_to_color(val);
                free(screens[i - 1].c[c].name);
                if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
-                       errx(1, "strdup");
+                       err(1, "strdup");
        } else if (i == -1) {
                for (i = 0; i < ScreenCount(display); i++) {
                        screens[i].c[c].color = name_to_color(val);
                        free(screens[i].c[c].name);
                        if ((screens[i].c[c].name = strdup(val)) == NULL)
-                               errx(1, "strdup");
+                               err(1, "strdup");
                }
        } else
-               errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
+               errx(1, "invalid screen index: %d out of bounds (maximum %d)",
                    i, ScreenCount(display));
 }
 
@@ -1187,14 +1203,14 @@ custom_region(char *val)
 
        if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
                errx(1, "invalid custom region, "
-                   "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
+                   "should be 'screen[<n>]:<n>x<n>+<n>+<n>");
        if (sidx < 1 || sidx > ScreenCount(display))
-               errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
+               errx(1, "invalid screen index: %d out of bounds (maximum %d)",
                    sidx, ScreenCount(display));
        sidx--;
 
        if (w < 1 || h < 1)
-               errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
+               errx(1, "region %ux%u+%u+%u too small", w, h, x, y);
 
        if (x > DisplayWidth(display, sidx) ||
            y > DisplayHeight(display, sidx) ||
@@ -1225,7 +1241,7 @@ socket_setnonblock(int fd)
 void
 bar_print(struct swm_region *r, char *s)
 {
-       int                     textwidth, x;
+       int                     textwidth, x = 0;
        size_t          len;
 
        XClearWindow(display, r->bar_window);
@@ -1359,6 +1375,7 @@ 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];
@@ -1394,16 +1411,20 @@ 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, stack, s, cn, bar_ext, bar_vertext);
+                       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);
                }
        }
@@ -1456,9 +1477,9 @@ bar_refresh(void)
                socket_setnonblock(bar_pipe[0]);
                socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
                if (dup2(bar_pipe[0], 0) == -1)
-                       errx(1, "dup2");
+                       err(1, "dup2");
                if (dup2(bar_pipe[1], 1) == -1)
-                       errx(1, "dup2");
+                       err(1, "dup2");
                if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
                        err(1, "could not disable SIGPIPE");
                switch (bar_pid = fork()) {
@@ -1507,7 +1528,7 @@ bar_setup(struct swm_region *r)
                }
        }
        if (bar_fonts[i] == NULL)
-                       errx(1, "couldn't load font");
+               errx(1, "couldn't load font");
        if (bar_fs == NULL)
                errx(1, "couldn't create font structure");
 
@@ -1743,7 +1764,7 @@ restart(struct swm_region *r, union arg *args)
        /* disable alarm because the following code may not be interrupted */
        alarm(0);
        if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
-               errx(1, "can't disable alarm");
+               err(1, "can't disable alarm");
 
        bar_extra_stop();
        bar_extra = 1;
@@ -1926,12 +1947,12 @@ validate_win(struct ws_win *testwin)
        struct ws_win           *win;
        struct workspace        *ws;
        struct swm_region       *r;
-       int                     i, x, foundit = 0;
+       int                     i, x;
 
        if (testwin == NULL)
                return (0);
 
-       for (i = 0, foundit = 0; i < ScreenCount(display); i++)
+       for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        for (x = 0; x < SWM_WS_MAX; x++) {
                                ws = &r->s->ws[x];
@@ -1947,10 +1968,10 @@ validate_ws(struct workspace *testws)
 {
        struct swm_region       *r;
        struct workspace        *ws;
-       int                     foundit, i, x;
+       int                     i, x;
 
        /* validate all ws */
-       for (i = 0, foundit = 0; i < ScreenCount(display); i++)
+       for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        for (x = 0; x < SWM_WS_MAX; x++) {
                                ws = &r->s->ws[x];
@@ -2409,8 +2430,14 @@ focus_prev(struct ws_win *win)
        if (winfocus == NULL || winfocus == win)
                winfocus = TAILQ_NEXT(cur_focus, entry);
 done:
-       if (winfocus == winlostfocus || winfocus == NULL)
+       if (winfocus == winlostfocus || winfocus == NULL) {
+               /* update the bar so that title/class/name will be cleared. */
+               if (window_name_enabled || title_name_enabled ||
+                   title_class_enabled)
+                       bar_update();
+               
                return;
+       }
 
        focus_magic(winfocus);
 }
@@ -2422,6 +2449,7 @@ focus(struct swm_region *r, union arg *args)
        struct ws_win           *cur_focus = NULL;
        struct ws_win_list      *wl = NULL;
        struct workspace        *ws = NULL;
+       int                     all_iconics;
 
        if (!(r && r->ws))
                return;
@@ -2447,6 +2475,17 @@ focus(struct swm_region *r, union arg *args)
                return;
        ws = r->ws;
        wl = &ws->winlist;
+       if (TAILQ_EMPTY(wl))
+               return;
+       /* make sure there is at least one uniconified window */
+       all_iconics = 1;
+       TAILQ_FOREACH(winfocus, wl, entry)
+               if (winfocus->iconic == 0) {
+                       all_iconics = 0;
+                       break;
+               }
+       if (all_iconics)
+               return;
 
        winlostfocus = cur_focus;
 
@@ -2465,9 +2504,13 @@ focus(struct swm_region *r, union arg *args)
 
                /* skip iconics */
                if (winfocus && winfocus->iconic) {
-                       TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
+                       while (winfocus != cur_focus) {
+                               if (winfocus == NULL)
+                                       winfocus = TAILQ_LAST(wl, ws_win_list);
                                if (winfocus->iconic == 0)
                                        break;
+                               winfocus = TAILQ_PREV(winfocus, ws_win_list, entry);
+                       }
                }
                break;
 
@@ -2479,9 +2522,13 @@ focus(struct swm_region *r, union arg *args)
 
                /* skip iconics */
                if (winfocus && winfocus->iconic) {
-                       TAILQ_FOREACH(winfocus, wl, entry)
+                       while (winfocus != cur_focus) {
+                               if (winfocus == NULL)
+                                       winfocus = TAILQ_FIRST(wl);
                                if (winfocus->iconic == 0)
                                        break;
+                               winfocus = TAILQ_NEXT(winfocus, entry);
+                       }
                }
                break;
 
@@ -2494,8 +2541,14 @@ focus(struct swm_region *r, union arg *args)
        default:
                return;
        }
-       if (winfocus == winlostfocus || winfocus == NULL)
+       if (winfocus == winlostfocus || winfocus == NULL) {
+               /* update the bar so that title/class/name will be cleared. */
+               if (window_name_enabled || title_name_enabled ||
+                   title_class_enabled)
+                       bar_update();
+
                return;
+       }
 
        focus_magic(winfocus);
 }
@@ -2504,13 +2557,10 @@ void
 cycle_layout(struct swm_region *r, union arg *args)
 {
        struct workspace        *ws = r->ws;
-       struct ws_win           *winfocus;
        union arg               a;
 
        DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
 
-       winfocus = ws->focus;
-
        ws->cur_layout++;
        if (ws->cur_layout->l_stack == NULL)
                ws->cur_layout = &layouts[0];
@@ -2543,12 +2593,17 @@ void
 stack(void) {
        struct swm_geometry     g;
        struct swm_region       *r;
-       int                     i, j;
+       int                     i;
+#ifdef SWM_DEBUG
+       int j;
+#endif
 
        DNPRINTF(SWM_D_STACK, "stack\n");
 
        for (i = 0; i < ScreenCount(display); i++) {
+#ifdef SWM_DEBUG
                j = 0;
+#endif
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
                        DNPRINTF(SWM_D_STACK, "stacking workspace %d "
                            "(screen %d, region %d)\n", r->ws->idx, i, j++);
@@ -3084,7 +3139,7 @@ send_to_ws(struct swm_region *r, union arg *args)
        unsigned char           ws_idx_str[SWM_PROPLEN];
        union arg               a;
 
-       if (r && r->ws)
+       if (r && r->ws && r->ws->focus)
                win = r->ws->focus;
        else
                return;
@@ -3112,6 +3167,8 @@ send_to_ws(struct swm_region *r, union arg *args)
        unmap_window(win);
        TAILQ_REMOVE(&ws->winlist, win, entry);
        TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
+       if (TAILQ_EMPTY(&ws->winlist))
+               r->ws->focus = NULL;
        win->ws = nws;
 
        /* Try to update the window's workspace property */
@@ -3233,7 +3290,7 @@ uniconify(struct swm_region *r, union arg *args)
                if (win->iconic == 0)
                        continue;
 
-               name = get_win_name(display, win->id, a_wmname, a_string,
+               name = get_win_name(display, win->id, a_netwmname, a_utf8_string,
                    &len);
                if (name == NULL)
                        continue;
@@ -3245,6 +3302,139 @@ uniconify(struct swm_region *r, union arg *args)
 }
 
 void
+name_workspace(struct swm_region *r, union arg *args)
+{
+       FILE                    *lfile;
+
+       DNPRINTF(SWM_D_MISC, "name_workspace\n");
+
+       if (r == NULL)
+               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_workspace(struct swm_region *r, union arg *args)
+{
+       int                     i;
+       struct workspace        *ws;
+       FILE                    *lfile;
+
+       DNPRINTF(SWM_D_MISC, "search_workspace\n");
+
+       if (r == NULL)
+               return;
+
+       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;
+
+       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 : ""));
+       }
+
+       fclose(lfile);
+}
+
+void
+search_win_cleanup(void)
+{
+       struct search_window    *sw = NULL;
+
+       while ((sw = TAILQ_FIRST(&search_wl)) != NULL) {
+               XDestroyWindow(display, sw->indicator);
+               TAILQ_REMOVE(&search_wl, sw, entry);
+               free(sw);
+       }
+}
+
+void
+search_win(struct swm_region *r, union arg *args)
+{
+       struct ws_win           *win = NULL;
+       struct search_window    *sw = NULL;
+       Window                  w;
+       GC                      gc;
+       XGCValues               gcv;
+       int                     i;
+       char                    s[8];
+       FILE                    *lfile;
+       size_t                  len;
+       int                     textwidth;
+
+       DNPRINTF(SWM_D_MISC, "search_win\n");
+
+       search_r = r;
+       search_resp_action = SWM_SEARCH_SEARCH_WINDOW;
+
+       spawn_select(r, args, "search", &searchpid);
+
+       if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+               return;
+
+       TAILQ_INIT(&search_wl);
+
+       i = 1;
+       TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+               if (win->iconic == 1)
+                       continue;
+
+               sw = calloc(1, sizeof(struct search_window));
+               if (sw == NULL) {
+                       fprintf(stderr, "search_win: calloc: %s", strerror(errno));
+                       fclose(lfile);
+                       search_win_cleanup();
+                       return;
+               }
+               sw->idx = i;
+               sw->win = win;
+
+               snprintf(s, sizeof s, "%d", i);
+               len = strlen(s);
+               textwidth = XTextWidth(bar_fs, s, len);
+
+               w = XCreateSimpleWindow(display,
+                   win->id, 0, 0, textwidth + 12,
+                   bar_fs->ascent + bar_fs->descent + 4, 1,
+                   r->s->c[SWM_S_COLOR_UNFOCUS].color,
+                   r->s->c[SWM_S_COLOR_FOCUS].color);
+
+               sw->indicator = w;
+               TAILQ_INSERT_TAIL(&search_wl, sw, entry);
+
+               gc = XCreateGC(display, w, 0, &gcv);
+               XSetFont(display, gc, bar_fs->fid);
+               XMapRaised(display, w);
+               XSetForeground(display, gc, r->s->c[SWM_S_COLOR_BAR].color);
+
+               XDrawString(display, w, gc, 6, bar_fs->ascent + 2, s, len);
+
+               fprintf(lfile, "%d\n", i);
+               i++;
+       }
+
+       fclose(lfile);
+}
+
+void
 search_resp_uniconify(char *resp, unsigned long len)
 {
        unsigned char           *name;
@@ -3256,7 +3446,7 @@ search_resp_uniconify(char *resp, unsigned long len)
        TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
                if (win->iconic == 0)
                        continue;
-               name = get_win_name(display, win->id, a_wmname, a_string, &len);
+               name = get_win_name(display, win->id, a_netwmname, a_utf8_string, &len);
                if (name == NULL)
                        continue;
                if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
@@ -3274,6 +3464,96 @@ search_resp_uniconify(char *resp, unsigned long len)
        }
 }
 
+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;
+               }
+       }
+}
+
+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;
+       }
+       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);
+}
+
+void
+search_resp_search_window(char *resp, unsigned long len)
+{
+       char                    *s;
+       int                     idx;
+       const char              *errstr;
+       struct search_window    *sw;
+
+       DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp %s\n", resp);
+
+       s = strdup(resp);
+       if (!s) {
+               DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s",
+                   strerror(errno));
+               return;
+       }
+
+       idx = strtonum(s, 1, INT_MAX, &errstr);
+       if (errstr) {
+               DNPRINTF(SWM_D_MISC, "window idx is %s: %s",
+                   errstr, s);
+               free(s);
+               return;
+       }
+       free(s);
+
+       TAILQ_FOREACH(sw, &search_wl, entry)
+               if (idx == sw->idx) {
+                       focus_win(sw->win);
+                       break;
+               }
+}
+
 #define MAX_RESP_LEN   1024
 
 void
@@ -3299,15 +3579,33 @@ search_do_resp(void)
                goto done;
        }
        resp[rbytes] = '\0';
+
+       /* XXX:
+        * Older versions of dmenu (Atleast pre 4.4.1) do not send a
+        * newline, so work around that by sanitizing the resp now.
+        */
+       resp[strcspn(resp, "\n")] = '\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;
+       case SWM_SEARCH_SEARCH_WINDOW:
+               search_resp_search_window(resp, len);
+               break;
        }
 
 done:
+       if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW)
+               search_win_cleanup();
+
        search_resp_action = SWM_SEARCH_NONE;
        close(select_resp_pipe[0]);
        free(resp);
@@ -3391,25 +3689,57 @@ floating_toggle(struct swm_region *r, union arg *args)
 }
 
 void
-resize_window(struct ws_win *win, int center)
+constrain_window(struct ws_win *win, struct swm_region *r, int resizable)
+{
+       if (win->g.x + win->g.w > r->g.x + r->g.w - border_width) {
+               if (resizable) 
+                       win->g.w = r->g.x + r->g.w - win->g.x - border_width;
+               else
+                       win->g.x = r->g.x + r->g.w - win->g.w - border_width;
+       }
+       
+       if (win->g.x < r->g.x - border_width) {
+               if (resizable)
+                       win->g.w -= r->g.x - win->g.x - border_width;
+
+               win->g.x = r->g.x - border_width;
+       }
+
+       if (win->g.y + win->g.h > r->g.y + r->g.h - border_width) {
+               if (resizable)
+                       win->g.h = r->g.y + r->g.h - win->g.y - border_width;
+               else
+                       win->g.y = r->g.y + r->g.h - win->g.h - border_width;
+       }
+       
+       if (win->g.y < r->g.y - border_width) {
+               if (resizable)
+                       win->g.h -= r->g.y - win->g.y - border_width;
+
+               win->g.y = r->g.y - border_width;
+       }
+
+       if (win->g.w < 1)
+               win->g.w = 1;
+       if (win->g.h < 1)
+               win->g.h = 1;
+}
+
+void
+update_window(struct ws_win *win)
 {
        unsigned int            mask;
        XWindowChanges          wc;
-       struct swm_region       *r;
 
-       r = root_to_region(win->wa.root);
        bzero(&wc, sizeof wc);
-       mask = CWBorderWidth | CWWidth | CWHeight;
+       mask = CWBorderWidth | CWWidth | CWHeight | CWX | CWY;
        wc.border_width = border_width;
+       wc.x = win->g.x;
+       wc.y = win->g.y;
        wc.width = win->g.w;
        wc.height = win->g.h;
-       if (center == SWM_ARG_ID_CENTER) {
-               wc.x = (WIDTH(r) - win->g.w) / 2 - border_width;
-               wc.y = (HEIGHT(r) - win->g.h) / 2 - border_width;
-               mask |= CWX | CWY;
-       }
 
-       DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
+       DNPRINTF(SWM_D_STACK, "update_window: win %lu x %d y %d w %d h %d\n",
            win->id, wc.x, wc.y, wc.width, wc.height);
 
        XConfigureWindow(display, win->id, mask, &wc);
@@ -3423,8 +3753,15 @@ resize(struct ws_win *win, union arg *args)
        XEvent                  ev;
        Time                    time = 0;
        struct swm_region       *r = NULL;
-       int                     relx, rely;
        int                     resize_step = 0;
+       Window                  rr, cr;
+       int                     x, y, wx, wy;
+       unsigned int            mask;
+       struct swm_geometry     g;
+       int                     top = 0, left = 0;
+       int                     dx, dy;
+       Cursor                  cursor;
+       unsigned int            shape; /* cursor style */
 
        if (win == NULL)
                return;
@@ -3467,7 +3804,8 @@ resize(struct ws_win *win, union arg *args)
                break;
        }
        if (resize_step) {
-               resize_window(win, 0);
+               constrain_window(win, r, 1);
+               update_window(win);
                store_float_geom(win,r);
                return;
        }
@@ -3475,22 +3813,33 @@ resize(struct ws_win *win, union arg *args)
        if (focus_mode == SWM_FOCUS_DEFAULT)
                drain_enter_notify();
 
-       if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
-           GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
-               return;
+       /* get cursor offset from window root */
+       if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask))
+           return;
 
-       /* place pointer at bottom left corner or nearest point inside r */
-       if ( win->g.x + win->g.w < r->g.x + r->g.w - 1)
-               relx = win->g.w - 1;
-       else
-               relx = r->g.x + r->g.w - win->g.x - 1;
+       g = win->g;
 
-       if ( win->g.y + win->g.h < r->g.y + r->g.h - 1)
-               rely = win->g.h - 1;
-       else
-               rely = r->g.y + r->g.h - win->g.y - 1;
+       if (wx < win->g.w / 2) 
+               left = 1;
+
+       if (wy < win->g.h / 2)
+               top = 1;
+
+       if (args->id == SWM_ARG_ID_CENTER)
+               shape = XC_sizing;
+       else if (top)
+               shape = (left) ? XC_top_left_corner : XC_top_right_corner;
+       else 
+               shape = (left) ? XC_bottom_left_corner : XC_bottom_right_corner;
+
+       cursor = XCreateFontCursor(display, shape);
+
+       if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
+           GrabModeAsync, None, cursor, CurrentTime) != GrabSuccess) {
+               XFreeCursor(display, cursor);
+               return;
+       }
 
-       XWarpPointer(display, None, win->id, 0, 0, 0, 0, relx, rely);
        do {
                XMaskEvent(display, MOUSEMASK | ExposureMask |
                    SubstructureRedirectMask, &ev);
@@ -3501,38 +3850,69 @@ resize(struct ws_win *win, union arg *args)
                        handler[ev.type](&ev);
                        break;
                case MotionNotify:
-                       /* do not allow resize outside of region */
-                       if (    ev.xmotion.x_root < r->g.x ||
-                               ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
-                               ev.xmotion.y_root < r->g.y ||
-                               ev.xmotion.y_root > r->g.y + r->g.h - 1)
-                               continue;
+                       /* cursor offset/delta from start of the operation */
+                       dx = ev.xmotion.x_root - x;
+                       dy = ev.xmotion.y_root - y;
+
+                       /* vertical */
+                       if (top)
+                               dy = -dy;
 
-                       if (ev.xmotion.x <= 1)
-                               ev.xmotion.x = 1;
-                       if (ev.xmotion.y <= 1)
-                               ev.xmotion.y = 1;
-                       win->g.w = ev.xmotion.x + 1;
-                       win->g.h = ev.xmotion.y + 1;
+                       if (args->id == SWM_ARG_ID_CENTER) {
+                               if (g.h / 2 + dy < 1)
+                                       dy = 1 - g.h / 2;
+
+                               win->g.y = g.y - dy;
+                               win->g.h = g.h + 2 * dy;
+                       } else {
+                               if (g.h + dy < 1)
+                                       dy = 1 - g.h;
+
+                               if (top)
+                                       win->g.y = g.y - dy;
+
+                               win->g.h = g.h + dy;
+                       }
+
+                       /* horizontal */
+                       if (left) 
+                               dx = -dx;
+
+                       if (args->id == SWM_ARG_ID_CENTER) {
+                               if (g.w / 2 + dx < 1)
+                                       dx = 1 - g.w / 2;
+
+                               win->g.x = g.x - dx;
+                               win->g.w = g.w + 2 * dx;
+                       } else {
+                               if (g.w + dx < 1)
+                                       dx = 1 - g.w;
+
+                               if (left)
+                                       win->g.x = g.x - dx;
+
+                               win->g.w = g.w + dx;
+                       }
+
+                       constrain_window(win, r, 1);
 
                        /* not free, don't sync more than 120 times / second */
                        if ((ev.xmotion.time - time) > (1000 / 120) ) {
                                time = ev.xmotion.time;
                                XSync(display, False);
-                               resize_window(win, args->id);
+                               update_window(win);
                        }
                        break;
                }
        } while (ev.type != ButtonRelease);
        if (time) {
                XSync(display, False);
-               resize_window(win, args->id);
+               update_window(win);
        }
        store_float_geom(win,r);
 
-       XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
-           win->g.h - 1);
        XUngrabPointer(display, CurrentTime);
+       XFreeCursor(display, cursor);
 
        /* drain events */
        drain_enter_notify();
@@ -3551,27 +3931,6 @@ resize_step(struct swm_region *r, union arg *args)
        resize(win, args);
 }
 
-
-void
-move_window(struct ws_win *win)
-{
-       unsigned int            mask;
-       XWindowChanges          wc;
-       struct swm_region       *r;
-
-       r = root_to_region(win->wa.root);
-       bzero(&wc, sizeof wc);
-       mask = CWX | CWY;
-       wc.x = win->g.x;
-       wc.y = win->g.y;
-       wc.border_width = border_width;
-
-       DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
-           win->id, wc.x, wc.y, wc.width, wc.height);
-
-       XConfigureWindow(display, win->id, mask, &wc);
-}
-
 #define SWM_MOVE_STEPS (50)
 
 void
@@ -3582,6 +3941,10 @@ move(struct ws_win *win, union arg *args)
        int                     move_step = 0;
        struct swm_region       *r = NULL;
 
+       Window                  rr, cr;
+       int                     x, y, wx, wy;
+       unsigned int            mask;
+
        if (win == NULL)
                return;
        r = win->ws->r;
@@ -3625,16 +3988,21 @@ move(struct ws_win *win, union arg *args)
                break;
        }
        if (move_step) {
-               move_window(win);
+               constrain_window(win, r, 0);
+               update_window(win);
                store_float_geom(win,r);
                return;
        }
 
-
        if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
-           GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
+           GrabModeAsync, None, XCreateFontCursor(display, XC_fleur),
+           CurrentTime) != GrabSuccess)
                return;
-       XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
+
+       /* get cursor offset from window root */
+       if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask))
+           return;
+
        do {
                XMaskEvent(display, MOUSEMASK | ExposureMask |
                    SubstructureRedirectMask, &ev);
@@ -3645,31 +4013,25 @@ move(struct ws_win *win, union arg *args)
                        handler[ev.type](&ev);
                        break;
                case MotionNotify:
-                       /* don't allow to move window origin out of region */
-                       if (    ev.xmotion.x_root < r->g.x ||
-                               ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
-                               ev.xmotion.y_root < r->g.y ||
-                               ev.xmotion.y_root > r->g.y + r->g.h - 1)
-                               continue;
+                       win->g.x = ev.xmotion.x_root - wx - border_width;
+                       win->g.y = ev.xmotion.y_root - wy - border_width;
 
-                       win->g.x = ev.xmotion.x_root - border_width;
-                       win->g.y = ev.xmotion.y_root - border_width;
+                       constrain_window(win, r, 0);
 
                        /* not free, don't sync more than 120 times / second */
                        if ((ev.xmotion.time - time) > (1000 / 120) ) {
                                time = ev.xmotion.time;
                                XSync(display, False);
-                               move_window(win);
+                               update_window(win);
                        }
                        break;
                }
        } while (ev.type != ButtonRelease);
        if (time) {
                XSync(display, False);
-               move_window(win);
+               update_window(win);
        }
        store_float_geom(win,r);
-       XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
        XUngrabPointer(display, CurrentTime);
 
        /* drain events */
@@ -3762,6 +4124,9 @@ enum keyfuncid {
        kf_move_right,
        kf_move_up,
        kf_move_down,
+       kf_name_workspace,
+       kf_search_workspace,
+       kf_search_win,
        kf_dumpwins, /* MUST BE LAST */
        kf_invalid
 };
@@ -3850,6 +4215,9 @@ struct keyfunc {
        { "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} },
+       { "search_win",         search_win,     {0} },
        { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
        { "invalid key func",   NULL,           {0} },
 };
@@ -4027,9 +4395,9 @@ spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
                break;
        case 0: /* child */
                if (dup2(select_list_pipe[0], 0) == -1)
-                       errx(1, "dup2");
+                       err(1, "dup2");
                if (dup2(select_resp_pipe[1], 1) == -1)
-                       errx(1, "dup2");
+                       err(1, "dup2");
                close(select_list_pipe[1]);
                close(select_resp_pipe[0]);
                spawn(r->ws->idx, &a, 0);
@@ -4189,6 +4557,13 @@ setup_spawn(void)
                                        " -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 */
@@ -4453,6 +4828,9 @@ setup_keys(void)
        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);
+       setkeybinding(MODKEY,           XK_f,           kf_search_win,  NULL);
 #ifdef SWM_DEBUG
        setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
 #endif
@@ -4802,7 +5180,7 @@ setconfvalue(char *selector, char *value, int flags)
                else if (!strcmp(value, "synergy"))
                        focus_mode = SWM_FOCUS_SYNERGY;
                else
-                       err(1, "focus_mode");
+                       errx(1, "focus_mode");
                break;
        case SWM_S_DISABLE_BORDER:
                disable_border = atoi(value);
@@ -4893,10 +5271,10 @@ setautorun(char *selector, char *value, int flags)
 
        bzero(s, sizeof s);
        if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
-               errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'\n");
+               errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'");
        ws_id--;
        if (ws_id < 0 || ws_id >= SWM_WS_MAX)
-               errx(1, "autorun: invalid workspace %d\n", ws_id + 1);
+               errx(1, "autorun: invalid workspace %d", ws_id + 1);
 
        /*
         * This is a little intricate
@@ -4958,10 +5336,10 @@ setlayout(char *selector, char *value, int flags)
            &ws_id, &mg, &ma, &si, &raise, s) != 6)
                errx(1, "invalid layout entry, should be 'ws[<idx>]:"
                    "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
-                   "<type>'\n");
+                   "<type>'");
        ws_id--;
        if (ws_id < 0 || ws_id >= SWM_WS_MAX)
-               errx(1, "layout: invalid workspace %d\n", ws_id + 1);
+               errx(1, "layout: invalid workspace %d", ws_id + 1);
 
        if (!strcasecmp(s, "vertical"))
                st = SWM_V_STACK;
@@ -4972,7 +5350,7 @@ setlayout(char *selector, char *value, int flags)
        else
                errx(1, "invalid layout entry, should be 'ws[<idx>]:"
                    "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
-                   "<type>'\n");
+                   "<type>'");
 
        for (i = 0; i < ScreenCount(display); i++) {
                ws = (struct workspace *)&screens[i].ws;
@@ -5096,7 +5474,7 @@ conf_load(char *filename, int keymapping)
                if (wordlen == 0) {
                        warnx("%s: line %zd: no option found",
                            filename, lineno);
-                       return (1);
+                       goto out;
                }
                optind = -1;
                for (i = 0; i < LENGTH(configopt); i++) {
@@ -5110,12 +5488,12 @@ conf_load(char *filename, int keymapping)
                if (optind == -1) {
                        warnx("%s: line %zd: unknown option %.*s",
                            filename, lineno, wordlen, cp);
-                       return (1);
+                       goto out;
                }
                if (keymapping && strcmp(opt->optname, "bind")) {
                        warnx("%s: line %zd: invalid option %.*s",
                            filename, lineno, wordlen, cp);
-                       return (1);
+                       goto out;
                }
                cp += wordlen;
                cp += strspn(cp, " \t\n"); /* eat whitespace */
@@ -5128,9 +5506,16 @@ conf_load(char *filename, int keymapping)
                                if (wordlen == 0) {
                                        warnx("%s: line %zd: syntax error",
                                            filename, lineno);
+                                       goto out;
+                               }
+
+                               if(asprintf(&optsub, "%.*s", wordlen, cp) ==
+                                   -1) {
+                                       warnx("%s: line %zd: unable to allocate"
+                                           "memory for selector", filename,
+                                           lineno);
                                        return (1);
                                }
-                               asprintf(&optsub, "%.*s", wordlen, cp);
                        }
                        cp += wordlen;
                        cp += strspn(cp, "] \t\n"); /* eat trailing */
@@ -5155,6 +5540,12 @@ conf_load(char *filename, int keymapping)
        DNPRINTF(SWM_D_CONF, "conf_load end\n");
 
        return (0);
+
+out:
+       free(line);
+       fclose(config);
+
+       return (1);
 }
 
 void
@@ -5288,7 +5679,7 @@ manage_window(Window id)
        }
 
        if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
-               errx(1, "calloc: failed to allocate memory for new window");
+               err(1, "calloc: failed to allocate memory for new window");
 
        win->id = id;
 
@@ -5698,6 +6089,11 @@ enternotify(XEvent *e)
            ev->window, ev->mode, ev->detail, ev->root, ev->subwindow,
            ev->same_screen, ev->focus, ev->state);
 
+       if (ev->mode != NotifyNormal) {
+               DNPRINTF(SWM_D_EVENT, "ignoring enternotify: generated by cursor grab.\n");
+               return;
+       }
+
        switch (focus_mode) {
        case SWM_FOCUS_DEFAULT:
                break;
@@ -5897,8 +6293,8 @@ propertynotify(XEvent *e)
        struct ws_win           *win;
        XPropertyEvent          *ev = &e->xproperty;
 
-       DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
-           ev->window);
+       DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu atom:%s\n",
+           ev->window, XGetAtomName(display, ev->atom));
 
        win = find_window(ev->window);
        if (win == NULL)
@@ -5913,8 +6309,8 @@ propertynotify(XEvent *e)
        }
 
        switch (ev->atom) {
-       case XA_WM_NORMAL_HINTS:
 #if 0
+       case XA_WM_NORMAL_HINTS:
                long            mask;
                XGetWMNormalHints(display, win->id, &win->sh, &mask);
                fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
@@ -5926,6 +6322,11 @@ propertynotify(XEvent *e)
                XMoveResizeWindow(display, win->id,
                    win->g.x, win->g.y, win->g.w, win->g.h);
 #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();
                break;
@@ -6120,7 +6521,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
                        ws = r->ws;
        } else
                if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
-                       errx(1, "calloc: failed to allocate memory for screen");
+                       err(1, "calloc: failed to allocate memory for screen");
 
        /* if we don't have a workspace already, find one */
        if (ws == NULL) {
@@ -6132,7 +6533,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
        }
 
        if (ws == NULL)
-               errx(1, "no free workspaces\n");
+               errx(1, "no free workspaces");
 
        X(r) = x;
        Y(r) = y;
@@ -6221,7 +6622,7 @@ screenchange(XEvent *e) {
                if (screens[i].root == xe->root)
                        break;
        if (i >= ScreenCount(display))
-               errx(1, "screenchange: screen not found\n");
+               errx(1, "screenchange: screen not found");
 
        /* brute force for now, just re-enumerate the regions */
        scan_xrandr(i);
@@ -6289,7 +6690,7 @@ setup_screens(void)
 
        if ((screens = calloc(ScreenCount(display),
             sizeof(struct swm_screen))) == NULL)
-               errx(1, "calloc: screens");
+               err(1, "calloc: screens");
 
        /* initial Xrandr setup */
        xrandr_support = XRRQueryExtension(display,
@@ -6322,6 +6723,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;
@@ -6359,7 +6761,7 @@ setup_globals(void)
        if ((spawn_term[0] = strdup("xterm")) == NULL)
                err(1, "setup_globals: strdup");
        if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
-               errx(1, "strdup");
+               err(1, "strdup");
 }
 
 void
@@ -6434,6 +6836,7 @@ main(int argc, char *argv[])
        adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
        takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False);
        a_wmname = XInternAtom(display, "WM_NAME", False);
+       a_netwmname = XInternAtom(display, "_NET_WM_NAME", False);
        a_utf8_string = XInternAtom(display, "UTF8_STRING", False);
        a_string = XInternAtom(display, "STRING", False);
        a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False);