JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix 'bind[] = ...' not unbinding as expected.
[spectrwm.git] / spectrwm.c
index 41bd6d4..251b746 100644 (file)
@@ -373,6 +373,7 @@ int          bar_justify = SWM_BAR_JUSTIFY_LEFT;
 char            *bar_format = NULL;
 int             stack_enabled = 1;
 int             clock_enabled = 1;
+int             iconic_enabled = 0;
 int             urgent_enabled = 0;
 char           *clock_format = NULL;
 int             window_class_enabled = 0;
@@ -869,6 +870,7 @@ RB_HEAD(key_tree, key);
 
 /* function prototypes */
 void    adjust_font(struct ws_win *);
+char   *argsep(char **);
 void    bar_cleanup(struct swm_region *);
 void    bar_extra_setup(void);
 void    bar_extra_stop(void);
@@ -2077,6 +2079,8 @@ bar_workspace_name(char *s, size_t sz, struct swm_region *r)
 void
 bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
 {
+       struct ws_win           *w;
+
        /* if format provided, just copy the buffers */
        if (bar_format != NULL) {
                strlcpy(fmtnew, fmtexp, sz);
@@ -2094,6 +2098,15 @@ bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
        /* only show the workspace name if there's actually one */
        if (r != NULL && r->ws != NULL && r->ws->name != NULL)
                strlcat(fmtnew, "<+D>", sz);
+
+       /* If enabled, only show the iconic count if there are iconic wins. */
+       if (iconic_enabled && r != NULL && r->ws != NULL)
+               TAILQ_FOREACH(w, &r->ws->winlist, entry)
+                       if (w->iconic) {
+                               strlcat(fmtnew, "{+M}", sz);
+                               break;
+                       }
+
        strlcat(fmtnew, "+3<", sz);
 
        if (clock_enabled) {
@@ -2143,9 +2156,10 @@ char *
 bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
     size_t sz)
 {
+       struct ws_win           *w;
        char                    *ptr;
        char                    tmp[SWM_BAR_MAX];
-       int                     limit, size;
+       int                     limit, size, count;
        size_t                  len;
 
        /* reset strlcat(3) buffer */
@@ -2183,6 +2197,14 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
        case 'I':
                snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
                break;
+       case 'M':
+               count = 0;
+               TAILQ_FOREACH(w, &r->ws->winlist, entry)
+                       if (w->iconic)
+                               ++count;
+
+               snprintf(tmp, sizeof tmp, "%d", count);
+               break;
        case 'N':
                snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
                break;
@@ -6234,24 +6256,73 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name,
        free(real_args);
 }
 
+/* Argument tokenizer. */
+char *
+argsep(char **sp) {
+       int                     single_quoted = 0, double_quoted = 0;
+       char                    *arg, *cp, *next;
+
+       if (*sp == NULL)
+               return NULL;
+
+       /* Eat and move characters until end of argument is found. */
+       for (arg = next = cp = *sp; *cp != '\0'; ++cp) {
+               if (!double_quoted && *cp == '\'') {
+                       /* Eat single-quote. */
+                       single_quoted = !single_quoted;
+               } else if (!single_quoted && *cp == '"') {
+                       /* Eat double-quote. */
+                       double_quoted = !double_quoted;
+               } else if (!single_quoted && *cp == '\\' && *(cp + 1) == '"') {
+                       /* Eat backslash; copy escaped character to arg. */
+                       *next++ = *(++cp);
+               } else if (!single_quoted && !double_quoted && *cp == '\\' &&
+                   (*(cp + 1) == '\'' || *(cp + 1) == ' ')) {
+                       /* Eat backslash; move escaped character. */
+                       *next++ = *(++cp);
+               } else if (!single_quoted && !double_quoted &&
+                   (*cp == ' ' || *cp == '\t')) {
+                       /* Terminate argument. */
+                       *next++ = '\0';
+                       /* Point sp to beginning of next argument. */
+                       *sp = ++cp;
+                       break;
+               } else {
+                       /* Move regular character. */
+                       *next++ = *cp;
+               }
+       }
+
+       /* Terminate argument if end of string. */
+       if (*cp == '\0') {
+               *next = '\0';
+               *sp = NULL;
+       }
+
+       return arg;
+}
+
 void
 spawn_insert(const char *name, const char *args, int flags)
 {
-       char                    *arg, *cp, *ptr;
        struct spawn_prog       *sp;
+       char                    *arg, *dup, *ptr;
 
-       DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name);
+       DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s[%s]\n", name, args);
+
+       if (args == NULL || *args == '\0')
+               return;
 
        if ((sp = calloc(1, sizeof *sp)) == NULL)
                err(1, "spawn_insert: calloc");
        if ((sp->name = strdup(name)) == NULL)
                err(1, "spawn_insert: strdup");
 
-       /* convert the arguments to an argument list */
-       if ((ptr = cp = strdup(args)) == NULL)
+       /* Convert the arguments to an argument list. */
+       if ((ptr = dup = strdup(args)) == NULL)
                err(1, "spawn_insert: strdup");
-       while ((arg = strsep(&ptr, " \t")) != NULL) {
-               /* empty field; skip it */
+       while ((arg = argsep(&ptr)) != NULL) {
+               /* Null argument; skip it. */
                if (*arg == '\0')
                        continue;
 
@@ -6262,10 +6333,11 @@ spawn_insert(const char *name, const char *args, int flags)
                if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL)
                        err(1, "spawn_insert: strdup");
        }
-       free(cp);
+       free(dup);
 
        sp->flags = flags;
 
+       DNPRINTF(SWM_D_SPAWN, "arg %d: [%s]\n", sp->argc, sp->argv[sp->argc-1]);
        TAILQ_INSERT_TAIL(&spawns, sp, entry);
        DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
 }
@@ -6326,6 +6398,9 @@ setconfspawn(const char *selector, const char *value, int flags)
 {
        char            *args;
 
+       if (selector == NULL || strlen(selector) == 0)
+               return (1);
+
        args = expand_tilde(value);
 
        DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
@@ -6574,8 +6649,9 @@ setconfbinding(const char *selector, const char *value, int flags)
        /* suppress unused warning since var is needed */
        (void)flags;
 
-       DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
-       if (selector == NULL) {
+       DNPRINTF(SWM_D_KEY, "setconfbinding: enter selector: [%s], "
+           "value: [%s]\n", selector, value);
+       if (selector == NULL || strlen(selector) == 0) {
                DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
                if (parsekeys(value, mod_key, &mod, &ks) == 0) {
                        kfid = KF_INVALID;
@@ -6797,9 +6873,11 @@ updatenumlockmask(void)
                                    + j];
                                keycode = xcb_key_symbols_get_keycode(syms,
                                                XK_Num_Lock);
-                               if (kc == *keycode)
-                                       numlockmask = (1 << i);
-                               free(keycode);
+                               if (keycode) {
+                                       if (kc == *keycode)
+                                               numlockmask = (1 << i);
+                                       free(keycode);
+                               }
                        }
                }
                free(modmap_r);
@@ -7090,7 +7168,7 @@ setconfquirk(const char *selector, const char *value, int flags)
        /* suppress unused warning since var is needed */
        (void)flags;
 
-       if (selector == NULL)
+       if (selector == NULL || strlen(selector) == 0)
                return (0);
 
        if ((str = strdup(selector)) == NULL)
@@ -7138,19 +7216,19 @@ setconfquirk(const char *selector, const char *value, int flags)
 void
 setup_quirks(void)
 {
-       setquirk("MPlayer",             "xv",           "",     SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
-       setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  "",     SWM_Q_FLOAT);
-       setquirk("Firefox-bin",         "firefox-bin",  "",     SWM_Q_TRANSSZ);
-       setquirk("Firefox",             "Dialog",       "",     SWM_Q_FLOAT);
-       setquirk("Gimp",                "gimp",         "",     SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("XTerm",               "xterm",        "",     SWM_Q_XTERM_FONTADJ);
-       setquirk("xine",                "Xine Window",  "",     SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("Xitk",                "Xitk Combo",   "",     SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("xine",                "xine Panel",   "",     SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("Xitk",                "Xine Window",  "",     SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("xine",                "xine Video Fullscreen Window", "",     SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
-       setquirk("pcb",                 "pcb",          "",     SWM_Q_FLOAT);
-       setquirk("SDL_App",             "SDL_App",      "",     SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
+       setquirk("MPlayer",             "xv",           ".*",   SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
+       setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  ".*",   SWM_Q_FLOAT);
+       setquirk("Firefox-bin",         "firefox-bin",  ".*",   SWM_Q_TRANSSZ);
+       setquirk("Firefox",             "Dialog",       ".*",   SWM_Q_FLOAT);
+       setquirk("Gimp",                "gimp",         ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+       setquirk("XTerm",               "xterm",        ".*",   SWM_Q_XTERM_FONTADJ);
+       setquirk("xine",                "Xine Window",  ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+       setquirk("Xitk",                "Xitk Combo",   ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+       setquirk("xine",                "xine Panel",   ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+       setquirk("Xitk",                "Xine Window",  ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
+       setquirk("xine",                "xine Video Fullscreen Window", ".*",   SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
+       setquirk("pcb",                 "pcb",          ".*",   SWM_Q_FLOAT);
+       setquirk("SDL_App",             "SDL_App",      ".*",   SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
 }
 
 /* conf file stuff */
@@ -7179,6 +7257,7 @@ enum {
        SWM_S_FOCUS_CLOSE_WRAP,
        SWM_S_FOCUS_DEFAULT,
        SWM_S_FOCUS_MODE,
+       SWM_S_ICONIC_ENABLED,
        SWM_S_REGION_PADDING,
        SWM_S_SPAWN_ORDER,
        SWM_S_SPAWN_TERM,
@@ -7202,9 +7281,6 @@ setconfvalue(const char *selector, const char *value, int flags)
        int                     i, ws_id, num_screens;
        char                    *b, *str;
 
-       /* suppress unused warning since var is needed */
-       (void)selector;
-
        switch (flags) {
        case SWM_S_BAR_ACTION:
                free(bar_argv[0]);
@@ -7346,6 +7422,9 @@ setconfvalue(const char *selector, const char *value, int flags)
                else
                        errx(1, "focus_mode");
                break;
+       case SWM_S_ICONIC_ENABLED:
+               iconic_enabled = atoi(value);
+               break;
        case SWM_S_REGION_PADDING:
                region_padding = atoi(value);
                if (region_padding < 0)
@@ -7441,7 +7520,9 @@ setconfmodkey(const char *selector, const char *value, int flags)
 int
 setconfcolor(const char *selector, const char *value, int flags)
 {
-       setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
+       setscreencolor(value,
+           (selector == NULL || strlen(selector) == 0) ? -1 : atoi(selector),
+           flags);
        return (0);
 }
 
@@ -7649,6 +7730,7 @@ struct config_option configopt[] = {
        { "focus_close_wrap",           setconfvalue,   SWM_S_FOCUS_CLOSE_WRAP },
        { "focus_default",              setconfvalue,   SWM_S_FOCUS_DEFAULT },
        { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
+       { "iconic_enabled",             setconfvalue,   SWM_S_ICONIC_ENABLED },
        { "keyboard_mapping",           setkeymapping,  0 },
        { "layout",                     setlayout,      0 },
        { "modkey",                     setconfmodkey,  0 },
@@ -8745,10 +8827,22 @@ mapnotify(xcb_map_notify_event_t *e)
 void
 mappingnotify(xcb_mapping_notify_event_t *e)
 {
+       struct ws_win   *w;
+       int     i, j, num_screens;
+
        xcb_refresh_keyboard_mapping(syms, e);
 
-       if (e->request == XCB_MAPPING_KEYBOARD)
+       if (e->request == XCB_MAPPING_KEYBOARD) {
                grabkeys();
+
+               /* Regrab buttons on all managed windows. */
+               num_screens = get_screen_count();
+               for (i = 0; i < num_screens; i++)
+                       for (j = 0; j < workspace_limit; j++)
+                               TAILQ_FOREACH(w, &screens[i].ws[j].winlist,
+                                   entry)
+                                       grabbuttons(w);
+       }
 }
 
 void
@@ -8934,6 +9028,10 @@ propertynotify(xcb_property_notify_event_t *e)
                                focus_flush();
                        }
                } else if (e->state == XCB_PROPERTY_DELETE) {
+                       /* Reload floating geometry in case region changed. */
+                       if (win->floating)
+                               load_float_geom(win);
+
                        /* The window is no longer iconic, restack ws. */
                        if (focus_mode != SWM_FOCUS_FOLLOW)
                                ws->focus_pending = get_focus_magic(win);