JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
add xlock to list that isnt verified during startup
[spectrwm.git] / spectrwm.c
index 7597cd4..21310f7 100644 (file)
@@ -374,6 +374,8 @@ int          focus_default = SWM_STACK_TOP;
 int             spawn_position = SWM_STACK_TOP;
 int             disable_border = 0;
 int             border_width = 1;
+int             region_padding = 0;
+int             tile_gap = 0;
 int             verbose_layout = 0;
 time_t          time_started;
 pid_t           bar_pid;
@@ -384,6 +386,8 @@ int          bar_font_legacy = 1;
 char           *bar_fonts;
 XftColor        bar_font_color;
 struct passwd  *pwd;
+char           *startup_exception;
+unsigned int   nr_exceptions = 0;
 
 /* layout manager data */
 struct swm_geometry {
@@ -922,6 +926,7 @@ char        *get_notify_mode_label(uint8_t);
 #endif
 struct ws_win  *get_pointer_win(xcb_window_t);
 struct ws_win  *get_region_focus(struct swm_region *);
+int     get_region_index(struct swm_region *);
 xcb_screen_t   *get_screen(int);
 #ifdef SWM_DEBUG
 char   *get_stack_mode_name(uint8_t);
@@ -1050,6 +1055,8 @@ pid_t      window_get_pid(xcb_window_t);
 void    wkill(struct swm_region *, union arg *);
 void    workaround(void);
 void    xft_init(struct swm_region *);
+void   _add_startup_exception(const char *, va_list);
+void   add_startup_exception(const char *, ...);
 
 RB_PROTOTYPE(key_tree, key, entry, key_cmp);
 RB_GENERATE(key_tree, key, entry, key_cmp);
@@ -1174,6 +1181,27 @@ get_screen(int screen)
        return (NULL);
 }
 
+int
+get_region_index(struct swm_region *r)
+{
+       struct swm_region       *rr;
+       int                      ridx = 0;
+
+       if (r == NULL)
+               return -1;
+
+       TAILQ_FOREACH(rr, &r->s->rl, entry) {
+               if (rr == r)
+                       break;
+               ++ridx;
+       }
+
+       if (rr == NULL)
+               return -1;
+
+       return ridx;
+}
+
 void
 focus_flush(void)
 {
@@ -2214,8 +2242,15 @@ bar_draw(void)
                                continue;
                        }
 
-                       bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
-                       bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
+                       if (startup_exception)
+                               snprintf(fmtrep, sizeof fmtrep, "total "
+                                   "exceptions: %d, first exception: %s",
+                                   nr_exceptions,
+                                   startup_exception);
+                       else {
+                               bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
+                               bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
+                       }
                        if (bar_font_legacy)
                                bar_print_legacy(r, fmtrep);
                        else
@@ -3315,11 +3350,6 @@ set_region(struct swm_region *r)
        if (r == NULL)
                return;
 
-       /* Skip if only one region on this screen. */
-       rf = TAILQ_FIRST(&r->s->rl);
-       if (TAILQ_NEXT(rf, entry) == NULL)
-               goto out;
-
        rf = r->s->r_focus;
        /* Unfocus old region bar. */
        if (rf) {
@@ -3335,7 +3365,6 @@ set_region(struct swm_region *r)
        xcb_change_window_attributes(conn, r->bar->id,
            XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel);
 
-out:
        r->s->r_focus = r;
 }
 
@@ -3946,10 +3975,12 @@ stack(void) {
                        DNPRINTF(SWM_D_STACK, "stack: workspace: %d "
                            "(screen: %d, region: %d)\n", r->ws->idx, i, j++);
 
-                       /* start with screen geometry, adjust for bar */
+                       /* Adjust stack area for region bar and padding. */
                        g = r->g;
-                       g.w -= 2 * border_width;
-                       g.h -= 2 * border_width;
+                       g.x += region_padding;
+                       g.y += region_padding;
+                       g.w -= 2 * border_width + 2 * region_padding;
+                       g.h -= 2 * border_width + 2 * region_padding;
                        if (bar_enabled && r->ws->bar_enabled) {
                                if (!bar_at_bottom)
                                        g.y += bar_height;
@@ -4185,9 +4216,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        /*  stack all the tiled windows */
        i = j = 0, s = stacks;
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (win->transient || win->floating)
-                       continue;
-               if (win->iconic)
+               if (win->transient || win->floating || win->iconic)
                        continue;
 
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
@@ -4205,16 +4234,20 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        if (flip)
                                win_g.x = r_g.x;
                        else
-                               win_g.x += win_g.w + 2 * border_width;
+                               win_g.x += win_g.w + 2 * border_width +
+                                   tile_gap;
                        win_g.w = (r_g.w - msize -
-                           (stacks * 2 * border_width)) / stacks;
+                           (stacks * (2 * border_width + tile_gap))) / stacks;
                        if (s == 1)
                                win_g.w += (r_g.w - msize -
-                                   (stacks * 2 * border_width)) % stacks;
+                                   (stacks * (2 * border_width + tile_gap))) %
+                                   stacks;
                        s--;
                        j = 0;
                }
-               win_g.h = hrh - 2 * border_width;
+
+               win_g.h = hrh - 2 * border_width - tile_gap;
+
                if (rot) {
                        h_inc = win->sh.width_inc;
                        h_base = win->sh.base_width;
@@ -4222,6 +4255,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        h_inc = win->sh.height_inc;
                        h_base = win->sh.base_height;
                }
+
                if (j == colno - 1) {
                        win_g.h = hrh + extra;
                } else if (h_inc > 1 && h_inc < h_slice) {
@@ -4241,7 +4275,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                if (j == 0)
                        win_g.y = r_g.y;
                else
-                       win_g.y += last_h + 2 * border_width;
+                       win_g.y += last_h + 2 * border_width + tile_gap;
 
                if (disable_border && !(bar_enabled && ws->bar_enabled) &&
                    winno == 1){
@@ -4251,6 +4285,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                } else {
                        bordered = 1;
                }
+
                if (rot) {
                        if (X(win) != win_g.y || Y(win) != win_g.x ||
                            WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) {
@@ -4418,7 +4453,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 {
        struct swm_geometry     gg = *g;
        struct ws_win           *w, *win = NULL, *parent = NULL;
-       int                     winno, num_screens;
+       int                     winno;
 
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
 
@@ -4443,11 +4478,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
        DNPRINTF(SWM_D_STACK, "max_stack: win: 0x%x\n", win->id);
 
        /* maximize all top level windows */
-       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
        TAILQ_FOREACH(w, &ws->winlist, entry) {
                if (w->transient || w->iconic)
                        continue;
 
+               if (!w->mapped && w != win)
+                       map_window(w);
+
                if (w->floating && !w->floatmaxed) {
                        /*
                         * retain geometry for retrieval on exit from
@@ -4471,14 +4508,9 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 
                        update_window(w);
                }
-
-               /* Unmap unwanted windows if not multi-screen. */
-               if (num_screens <= 1 && outputs <= 1 && w != win &&
-                   w != parent && w->transient != win->id)
-                       unmap_window(w);
        }
 
-       /* If a parent exists, map it first. */
+       /* If a parent exists, map/raise it first. */
        if (parent) {
                map_window(parent);
 
@@ -4491,10 +4523,10 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                        }
        }
 
-       /* Map focused window. */
+       /* Map/raise focused window. */
        map_window(win);
 
-       /* Finally, map children of focus window. */
+       /* Finally, map/raise children of focus window. */
        TAILQ_FOREACH(w, &ws->winlist, entry)
                if (w->transient == win->id && !w->iconic) {
                        stack_floater(w, ws->r);
@@ -4691,7 +4723,7 @@ get_win_name(xcb_window_t win)
                        free(r);
                        /* Use WM_NAME instead; no UTF-8. */
                        c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME,
-                               XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
+                               XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
                        r = xcb_get_property_reply(conn, c, NULL);
 
                        if (!r)
@@ -4703,7 +4735,7 @@ get_win_name(xcb_window_t win)
                }
                if (r->length > 0)
                        name = strndup(xcb_get_property_value(r),
-                                  xcb_get_property_value_length(r));
+                                  xcb_get_property_value_length(r));
 
                free(r);
        }
@@ -5805,6 +5837,13 @@ spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name,
                            strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
                            == NULL)
                                err(1, "spawn_custom color unfocus");
+               } else if (!strcasecmp(ap, "$region_index")) {
+                       if (asprintf(&real_args[i], "%d",
+                           get_region_index(r) + 1) < 1)
+                               err(1, "spawn_custom region index");
+               } else if (!strcasecmp(ap, "$workspace_index")) {
+                       if (asprintf(&real_args[i], "%d", r->ws->idx + 1) < 1)
+                               err(1, "spawn_custom workspace index");
                } else {
                        /* no match --> copy as is */
                        if ((real_args[i] = strdup(ap)) == NULL)
@@ -5979,7 +6018,9 @@ setspawn(const char *name, const char *args)
 int
 setconfspawn(char *selector, char *value, int flags)
 {
-       char *args;
+       char            *args;
+       char            which[PATH_MAX];
+       size_t          i;
 
        /* suppress unused warning since var is needed */
        (void)flags;
@@ -5988,6 +6029,17 @@ setconfspawn(char *selector, char *value, int flags)
 
        DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
 
+       /* verify we have the goods */
+       snprintf(which, sizeof which, "which %s", value);
+       for (i = strlen("which "); i < strlen(which); i++)
+               if (which[i] == ' ') {
+                       which[i] = '\0';
+                       break;
+               }
+       if (flags == 0 && system(which) != 0)
+               add_startup_exception("could not find %s",
+                   &which[strlen("which ")]);
+
        setspawn(selector, args);
        free(args);
 
@@ -6000,10 +6052,6 @@ setup_spawn(void)
 {
        setconfspawn("term",            "xterm",                0);
        setconfspawn("spawn_term",      "xterm",                0);
-       setconfspawn("screenshot_all",  "screenshot.sh full",   0);
-       setconfspawn("screenshot_wind", "screenshot.sh window", 0);
-       setconfspawn("lock",            "xlock",                0);
-       setconfspawn("initscr",         "initscreen.sh",        0);
        setconfspawn("menu",            "dmenu_run"
                                        " -fn $bar_font"
                                        " -nb $bar_color"
@@ -6024,6 +6072,13 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
+       /* these are not verified for existence */
+       setconfspawn("lock",            "xlock",                1);
+       setconfspawn("screenshot_all",  "screenshot.sh full",   1);
+       setconfspawn("screenshot_wind", "screenshot.sh window", 1);
+       setconfspawn("initscr",         "initscreen.sh",        1);
+
 }
 
 /* key bindings */
@@ -6662,12 +6717,14 @@ enum {
        SWM_S_FOCUS_CLOSE_WRAP,
        SWM_S_FOCUS_DEFAULT,
        SWM_S_FOCUS_MODE,
+       SWM_S_REGION_PADDING,
        SWM_S_SPAWN_ORDER,
        SWM_S_SPAWN_TERM,
        SWM_S_SS_APP,
        SWM_S_SS_ENABLED,
        SWM_S_STACK_ENABLED,
        SWM_S_TERM_WIDTH,
+       SWM_S_TILE_GAP,
        SWM_S_TITLE_CLASS_ENABLED,
        SWM_S_TITLE_NAME_ENABLED,
        SWM_S_URGENT_ENABLED,
@@ -6817,6 +6874,11 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "focus_mode");
                break;
+       case SWM_S_REGION_PADDING:
+               region_padding = atoi(value);
+               if (region_padding < 0)
+                       region_padding = 0;
+               break;
        case SWM_S_SPAWN_ORDER:
                if (!strcmp(value, "first"))
                        spawn_position = SWM_STACK_BOTTOM;
@@ -6847,6 +6909,11 @@ setconfvalue(char *selector, char *value, int flags)
                if (term_width < 0)
                        term_width = 0;
                break;
+       case SWM_S_TILE_GAP:
+               tile_gap = atoi(value);
+               if (tile_gap < 0)
+                       tile_gap = 0;
+               break;
        case SWM_S_TITLE_CLASS_ENABLED:
                title_class_enabled = atoi(value);
                break;
@@ -7117,12 +7184,14 @@ struct config_option configopt[] = {
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
        { "region",                     setconfregion,  0 },
+       { "region_padding",             setconfvalue,   SWM_S_REGION_PADDING },
        { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
        { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
        { "spawn_position",             setconfvalue,   SWM_S_SPAWN_ORDER },
        { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
        { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
        { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
+       { "tile_gap",                   setconfvalue,   SWM_S_TILE_GAP },
        { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
        { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
        { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
@@ -7131,11 +7200,36 @@ struct config_option configopt[] = {
        { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
 };
 
+void
+_add_startup_exception(const char *fmt, va_list ap)
+{
+       if (vasprintf(&startup_exception, fmt, ap) == -1)
+               warn("%s: asprintf", __func__);
+}
+
+void
+add_startup_exception(const char *fmt, ...)
+{
+       va_list ap;
+
+       nr_exceptions++;
+
+       if (startup_exception)
+               return;
+
+       /* force bar to be enabled due to exception */
+       bar_enabled = 1;
+
+       va_start(ap, fmt);
+       _add_startup_exception(fmt, ap);
+       va_end(ap);
+}
+
 int
 conf_load(const char *filename, int keymapping)
 {
        FILE                    *config;
-       char                    *line, *cp, *optsub, *optval;
+       char                    *line = NULL, *cp, *optsub, *optval;
        size_t                  linelen, lineno = 0;
        int                     wordlen, i, optidx;
        struct config_option    *opt = NULL;
@@ -7152,6 +7246,9 @@ conf_load(const char *filename, int keymapping)
        }
 
        while (!feof(config)) {
+               if (line)
+                       free(line);
+
                if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
                    == NULL) {
                        if (ferror(config))
@@ -7163,15 +7260,14 @@ conf_load(const char *filename, int keymapping)
                cp += strspn(cp, " \t\n"); /* eat whitespace */
                if (cp[0] == '\0') {
                        /* empty line */
-                       free(line);
                        continue;
                }
                /* get config option */
                wordlen = strcspn(cp, "=[ \t\n");
                if (wordlen == 0) {
-                       warnx("%s: line %zd: no option found",
+                       add_startup_exception("%s: line %zd: no option found",
                            filename, lineno);
-                       goto out;
+                       continue;
                }
                optidx = -1;
                for (i = 0; i < LENGTH(configopt); i++) {
@@ -7183,17 +7279,20 @@ conf_load(const char *filename, int keymapping)
                        }
                }
                if (optidx == -1) {
-                       warnx("%s: line %zd: unknown option %.*s",
-                           filename, lineno, wordlen, cp);
-                       goto out;
+                       add_startup_exception("%s: line %zd: unknown option "
+                           "%.*s", filename, lineno, wordlen, cp);
+                       continue;
                }
                if (keymapping && opt && strcmp(opt->optname, "bind")) {
-                       warnx("%s: line %zd: invalid option %.*s",
-                           filename, lineno, wordlen, cp);
-                       goto out;
+                       add_startup_exception("%s: line %zd: invalid option "
+                           "%.*s", filename, lineno, wordlen, cp);
+                       continue;
                }
                cp += wordlen;
                cp += strspn(cp, " \t\n"); /* eat whitespace */
+
+               /* from here on out we call goto invalid to continue */
+
                /* get [selector] if any */
                optsub = NULL;
                if (*cp == '[') {
@@ -7201,17 +7300,17 @@ conf_load(const char *filename, int keymapping)
                        wordlen = strcspn(cp, "]");
                        if (*cp != ']') {
                                if (wordlen == 0) {
-                                       warnx("%s: line %zd: syntax error",
-                                           filename, lineno);
-                                       goto out;
+                                       add_startup_exception("%s: line %zd: "
+                                           "syntax error", filename, lineno);
+                                       goto invalid;
                                }
 
                                if (asprintf(&optsub, "%.*s", wordlen, cp) ==
                                    -1) {
-                                       warnx("%s: line %zd: unable to allocate"
-                                           "memory for selector", filename,
-                                           lineno);
-                                       goto out;
+                                       add_startup_exception("%s: line %zd: "
+                                           "unable to allocatememory for "
+                                           "selector", filename, lineno);
+                                       goto invalid;
                                }
                        }
                        cp += wordlen;
@@ -7220,27 +7319,36 @@ conf_load(const char *filename, int keymapping)
                cp += strspn(cp, "= \t\n"); /* eat trailing */
                /* get RHS value */
                optval = strdup(cp);
+               if (strlen(optval) == 0) {
+                       add_startup_exception("%s: line %zd: must supply value "
+                           "to %s", filename, lineno,
+                           configopt[optidx].optname);
+                       goto invalid;
+               }
                /* call function to deal with it all */
                if (configopt[optidx].func(optsub, optval,
-                   configopt[optidx].funcflags) != 0)
-                       errx(1, "%s: line %zd: invalid data for %s",
-                           filename, lineno, configopt[optidx].optname);
-               free(optval);
-               free(optsub);
-               free(line);
+                   configopt[optidx].funcflags) != 0) {
+                       add_startup_exception("%s: line %zd: invalid data for "
+                           "%s", filename, lineno, configopt[optidx].optname);
+                       goto invalid;
+               }
+invalid:
+               if (optval) {
+                       free(optval);
+                       optval = NULL;
+               }
+               if (optsub) {
+                       free(optsub);
+                       optsub = NULL;
+               }
        }
 
+       if (line)
+               free(line);
        fclose(config);
        DNPRINTF(SWM_D_CONF, "conf_load: end\n");
 
        return (0);
-
-out:
-       free(line);
-       fclose(config);
-       DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n");
-
-       return (1);
 }
 
 void
@@ -8662,6 +8770,9 @@ scan_xrandr(int i)
                xcb_destroy_window(conn, r->id);
                TAILQ_REMOVE(&screens[i].rl, r, entry);
                TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
+
+               if (r->s->r_focus == r)
+                       r->s->r_focus = NULL;
        }
        outputs = 0;
 
@@ -8737,9 +8848,18 @@ screenchange(xcb_randr_screen_change_notify_event_t *e)
        print_win_geom(e->root);
 #endif
        /* add bars to all regions */
-       for (i = 0; i < num_screens; i++)
+       for (i = 0; i < num_screens; i++) {
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        bar_setup(r);
+
+               if (screens[0].r_focus == NULL) {
+                       /* Focus on first region. */
+                       r = TAILQ_FIRST(&screens[0].rl);
+                       if (r)
+                               focus_region(r);
+               }
+       }
+
        stack();
        bar_draw();
        focus_flush();