JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Add new quirk NOFOCUSONMAP.
[spectrwm.git] / spectrwm.c
index 42c8bd2..059c2ac 100644 (file)
@@ -386,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 {
@@ -608,6 +610,8 @@ struct quirk {
 #define SWM_Q_XTERM_FONTADJ    (1<<3)  /* adjust xterm fonts when resizing */
 #define SWM_Q_FULLSCREEN       (1<<4)  /* remove border */
 #define SWM_Q_FOCUSPREV                (1<<5)  /* focus on caller */
+#define SWM_Q_NOFOCUSONMAP     (1<<6)  /* Don't focus on window when mapped. */
+#define SWM_Q_FOCUSONMAP_SINGLE        (1<<7)  /* Only focus if single win of type. */
 };
 TAILQ_HEAD(quirk_list, quirk);
 struct quirk_list              quirks = TAILQ_HEAD_INITIALIZER(quirks);
@@ -705,12 +709,15 @@ struct cursors {
        {"top_right_corner", XC_top_right_corner, XCB_CURSOR_NONE},
 };
 
+#define        SWM_SPAWN_OPTIONAL              0x1
+
 /* spawn */
 struct spawn_prog {
        TAILQ_ENTRY(spawn_prog) entry;
        char                    *name;
        int                     argc;
        char                    **argv;
+       int                     flags;
 };
 TAILQ_HEAD(spawn_list, spawn_prog);
 struct spawn_list              spawns = TAILQ_HEAD_INITIALIZER(spawns);
@@ -855,8 +862,7 @@ void         bar_print(struct swm_region *, const char *);
 void    bar_print_legacy(struct swm_region *, const char *);
 void    bar_replace(char *, char *, struct swm_region *, size_t);
 void    bar_replace_pad(char *, int *, size_t);
-char *  bar_replace_seq(char *, char *, struct swm_region *, size_t *,
-            size_t);
+char   *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t);
 void    bar_setup(struct swm_region *);
 void    bar_title_name(char *, size_t, struct swm_region *);
 void    bar_toggle(struct swm_region *, union arg *);
@@ -1007,7 +1013,7 @@ int        setkeymapping(char *, char *, int);
 int     setlayout(char *, char *, int);
 void    setquirk(const char *, const char *, unsigned long);
 void    setscreencolor(char *, int, int);
-void    setspawn(const char *, const char *);
+void    setspawn(const char *, const char *, int);
 void    setup_ewmh(void);
 void    setup_globals(void);
 void    setup_keys(void);
@@ -1024,15 +1030,15 @@ void     sort_windows(struct ws_win_list *);
 void    spawn(int, union arg *, int);
 void    spawn_custom(struct swm_region *, union arg *, const char *);
 int     spawn_expand(struct swm_region *, union arg *, const char *, char ***);
-void    spawn_insert(const char *, const char *);
+void    spawn_insert(const char *, const char *, int);
 void    spawn_remove(struct spawn_prog *);
-void    spawn_replace(struct spawn_prog *, const char *, const char *);
+void    spawn_replace(struct spawn_prog *, const char *, const char *, int);
 void    spawn_select(struct swm_region *, union arg *, const char *, int *);
 void    stack_config(struct swm_region *, union arg *);
 void    stack_floater(struct ws_win *, struct swm_region *);
 void    stack_master(struct workspace *, struct swm_geometry *, int, int);
 void    store_float_geom(struct ws_win *, struct swm_region *);
-char *  strdupsafe(const char *);
+char   *strdupsafe(const char *);
 void    swapwin(struct swm_region *, union arg *);
 void    switchws(struct swm_region *, union arg *);
 void    teardown_ewmh(void);
@@ -1045,6 +1051,7 @@ void       unmap_window(struct ws_win *);
 void    updatenumlockmask(void);
 void    update_modkey(unsigned int);
 void    update_window(struct ws_win *);
+void    validate_spawns(void);
 int     validate_win(struct ws_win *);
 int     validate_ws(struct workspace *);
 /*void  visibilitynotify(xcb_visibility_notify_event_t *);*/
@@ -1053,6 +1060,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);
@@ -2238,8 +2247,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
@@ -2528,7 +2544,8 @@ bar_setup(struct swm_region *r)
        r->bar->id = xcb_generate_id(conn);
        wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel;
        wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel;
-       wa[2] = XCB_EVENT_MASK_EXPOSURE;
+       wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION |
+           XCB_EVENT_MASK_POINTER_MOTION_HINT;
 
        xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->bar->id, r->s->root,
            X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar),
@@ -4471,7 +4488,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                if (w->transient || w->iconic)
                        continue;
 
-               if (!win->mapped && w != win)
+               if (!w->mapped && w != win)
                        map_window(w);
 
                if (w->floating && !w->floatmaxed) {
@@ -4712,7 +4729,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)
@@ -4724,7 +4741,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);
        }
@@ -5310,6 +5327,12 @@ resize(struct ws_win *win, union arg *args)
 
        focus_flush();
 
+       /* It's possible for win to have been freed during focus_flush(). */
+       if (validate_win(win)) {
+               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+               goto out;
+       }
+
        switch (args->id) {
        case SWM_ARG_ID_WIDTHSHRINK:
                WIDTH(win) -= SWM_RESIZE_STEPS;
@@ -5366,7 +5389,7 @@ resize(struct ws_win *win, union arg *args)
 
        xcb_flush(conn);
        resizing = 1;
-       while ((evt = xcb_wait_for_event(conn)) && resizing) {
+       while (resizing && (evt = xcb_wait_for_event(conn))) {
                switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
                case XCB_BUTTON_RELEASE:
                        DNPRINTF(SWM_D_EVENT, "resize: BUTTON_RELEASE\n");
@@ -5429,6 +5452,12 @@ resize(struct ws_win *win, union arg *args)
                        break;
                default:
                        event_handle(evt);
+
+                       /* It's possible for win to have been freed above. */
+                       if (validate_win(win)) {
+                               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+                               goto out;
+                       }
                        break;
                }
                free(evt);
@@ -5438,7 +5467,7 @@ resize(struct ws_win *win, union arg *args)
                xcb_flush(conn);
        }
        store_float_geom(win,r);
-
+out:
        xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
        free(xpr);
        DNPRINTF(SWM_D_EVENT, "resize: done.\n");
@@ -5497,6 +5526,12 @@ move(struct ws_win *win, union arg *args)
 
        focus_flush();
 
+       /* It's possible for win to have been freed during focus_flush(). */
+       if (validate_win(win)) {
+               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+               goto out;
+       }
+
        move_stp = 0;
        switch (args->id) {
        case SWM_ARG_ID_MOVELEFT:
@@ -5539,7 +5574,7 @@ move(struct ws_win *win, union arg *args)
 
        xcb_flush(conn);
        moving = 1;
-       while ((evt = xcb_wait_for_event(conn)) && moving) {
+       while (moving && (evt = xcb_wait_for_event(conn))) {
                switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
                case XCB_BUTTON_RELEASE:
                        DNPRINTF(SWM_D_EVENT, "move: BUTTON_RELEASE\n");
@@ -5561,6 +5596,12 @@ move(struct ws_win *win, union arg *args)
                        break;
                default:
                        event_handle(evt);
+
+                       /* It's possible for win to have been freed above. */
+                       if (validate_win(win)) {
+                               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+                               goto out;
+                       }
                        break;
                }
                free(evt);
@@ -5570,6 +5611,7 @@ move(struct ws_win *win, union arg *args)
                xcb_flush(conn);
        }
        store_float_geom(win, r);
+out:
        free(qpr);
        xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
        DNPRINTF(SWM_D_EVENT, "move: done.\n");
@@ -5914,7 +5956,7 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name,
 }
 
 void
-spawn_insert(const char *name, const char *args)
+spawn_insert(const char *name, const char *args, int flags)
 {
        char                    *arg, *cp, *ptr;
        struct spawn_prog       *sp;
@@ -5943,6 +5985,8 @@ spawn_insert(const char *name, const char *args)
        }
        free(cp);
 
+       sp->flags = flags;
+
        TAILQ_INSERT_TAIL(&spawns, sp, entry);
        DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
 }
@@ -5965,18 +6009,7 @@ spawn_remove(struct spawn_prog *sp)
 }
 
 void
-spawn_replace(struct spawn_prog *sp, const char *name, const char *args)
-{
-       DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name);
-
-       spawn_remove(sp);
-       spawn_insert(name, args);
-
-       DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n");
-}
-
-void
-setspawn(const char *name, const char *args)
+setspawn(const char *name, const char *args, int flags)
 {
        struct spawn_prog       *sp;
 
@@ -5985,38 +6018,31 @@ setspawn(const char *name, const char *args)
        if (name == NULL)
                return;
 
-       TAILQ_FOREACH(sp, &spawns, entry) {
+       /* Remove any old spawn under the same name. */
+       TAILQ_FOREACH(sp, &spawns, entry)
                if (!strcmp(sp->name, name)) {
-                       if (*args == '\0')
-                               spawn_remove(sp);
-                       else
-                               spawn_replace(sp, name, args);
-                       DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
-                       return;
+                       spawn_remove(sp);
+                       break;
                }
-       }
-       if (*args == '\0') {
+
+       if (*args != '\0')
+               spawn_insert(name, args, flags);
+       else
                warnx("error: setspawn: cannot find program: %s", name);
-               return;
-       }
 
-       spawn_insert(name, args);
        DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
 }
 
 int
 setconfspawn(char *selector, char *value, int flags)
 {
-       char *args;
-
-       /* suppress unused warning since var is needed */
-       (void)flags;
+       char            *args;
 
        args = expand_tilde(value);
 
        DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
 
-       setspawn(selector, args);
+       setspawn(selector, args, flags);
        free(args);
 
        DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n");
@@ -6024,20 +6050,57 @@ setconfspawn(char *selector, char *value, int flags)
 }
 
 void
+validate_spawns(void)
+{
+       struct spawn_prog       *sp;
+       char                    which[PATH_MAX];
+       size_t                  i;
+
+       struct key              *kp;
+
+       RB_FOREACH(kp, key_tree, &keys) {
+               if (kp->funcid != KF_SPAWN_CUSTOM)
+                       continue;
+
+               /* find program */
+               TAILQ_FOREACH(sp, &spawns, entry) {
+                       if (!strcasecmp(kp->spawn_name, sp->name))
+                               break;
+               }
+
+               if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL)
+                       continue;
+
+               /* verify we have the goods */
+               snprintf(which, sizeof which, "which %s", sp->argv[0]);
+               DNPRINTF(SWM_D_CONF, "validate_spawns: which %s\n",
+                   sp->argv[0]);
+               for (i = strlen("which "); i < strlen(which); i++)
+                       if (which[i] == ' ') {
+                               which[i] = '\0';
+                               break;
+                       }
+               if (system(which) != 0)
+                       add_startup_exception("could not find %s",
+                           &which[strlen("which ")]);
+       }
+}
+
+void
 setup_spawn(void)
 {
+       setconfspawn("lock",            "xlock",                0);
+
        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"
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
        setconfspawn("search",          "dmenu"
                                        " -i"
                                        " -fn $bar_font"
@@ -6045,6 +6108,7 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
        setconfspawn("name_workspace",  "dmenu"
                                        " -p Workspace"
                                        " -fn $bar_font"
@@ -6052,6 +6116,11 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
+        /* These are not verified for existence, even with a binding set. */
+       setconfspawn("screenshot_all",  "screenshot.sh full",   SWM_SPAWN_OPTIONAL);
+       setconfspawn("screenshot_wind", "screenshot.sh window", SWM_SPAWN_OPTIONAL);
+       setconfspawn("initscr",         "initscreen.sh",        SWM_SPAWN_OPTIONAL);
 }
 
 /* key bindings */
@@ -6225,8 +6294,8 @@ setconfbinding(char *selector, char *value, int flags)
        for (kfid = 0; kfid < KF_INVALID; (kfid)++) {
                if (strncasecmp(selector, keyfuncs[kfid].name,
                    SWM_FUNCNAME_LEN) == 0) {
-                       DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
-                           selector);
+                       DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match "
+                           "keyfunc\n", selector);
                        if (parsekeys(value, mod_key, &mod, &ks) == 0) {
                                setkeybinding(mod, ks, kfid, NULL);
                                return (0);
@@ -6237,8 +6306,8 @@ setconfbinding(char *selector, char *value, int flags)
        /* search by custom spawn name */
        TAILQ_FOREACH(sp, &spawns, entry) {
                if (strcasecmp(selector, sp->name) == 0) {
-                       DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
-                           selector);
+                       DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match "
+                           "spawn\n", selector);
                        if (parsekeys(value, mod_key, &mod, &ks) == 0) {
                                setkeybinding(mod, ks, KF_SPAWN_CUSTOM,
                                    sp->name);
@@ -6390,24 +6459,25 @@ clear_keys(void)
 int
 setkeymapping(char *selector, char *value, int flags)
 {
-       char                    keymapping_file[PATH_MAX];
+       char                    *keymapping_file;
 
        /* suppress unused warnings since vars are needed */
        (void)selector;
        (void)flags;
 
        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);
+
+       keymapping_file = expand_tilde(value);
+
        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();
        }
+
+       free(keymapping_file);
+
        DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
        return (0);
 }
@@ -6511,6 +6581,8 @@ const char *quirkname[] = {
        "XTERM_FONTADJ",
        "FULLSCREEN",
        "FOCUSPREV",
+       "NOFOCUSONMAP",
+       "FOCUSONMAP_SINGLE",
 };
 
 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
@@ -7173,11 +7245,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 = NULL;
        size_t                  linelen, lineno = 0;
        int                     wordlen, i, optidx;
        struct config_option    *opt = NULL;
@@ -7188,14 +7285,20 @@ conf_load(const char *filename, int keymapping)
                warnx("conf_load: no filename");
                return (1);
        }
+
+       DNPRINTF(SWM_D_CONF, "conf_load: open %s\n", filename);
+
        if ((config = fopen(filename, "r")) == NULL) {
                warn("conf_load: fopen: %s", filename);
                return (1);
        }
 
        while (!feof(config)) {
-               if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
-                   == NULL) {
+               if (line)
+                       free(line);
+
+               if ((line = fparseln(config, &linelen, &lineno, NULL,
+                   FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) {
                        if (ferror(config))
                                err(1, "%s", filename);
                        else
@@ -7205,15 +7308,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++) {
@@ -7225,17 +7327,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 == '[') {
@@ -7243,17 +7348,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;
@@ -7262,27 +7367,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
@@ -8146,7 +8260,7 @@ enternotify(xcb_enter_notify_event_t *e)
                        /* If no windows on pointer region, then focus root. */
                        r = root_to_region(e->root, SWM_CK_POINTER);
                        if (r == NULL) {
-                               DNPRINTF(SWM_D_EVENT, "enterntoify: "
+                               DNPRINTF(SWM_D_EVENT, "enternotify: "
                                    "NULL region; ignoring.\n");
                                return;
                        }
@@ -8161,7 +8275,7 @@ enternotify(xcb_enter_notify_event_t *e)
                focus_win(get_focus_magic(win));
        }
 
-       focus_flush();
+       xcb_flush(conn);
 }
 
 #ifdef SWM_DEBUG
@@ -8215,7 +8329,7 @@ mappingnotify(xcb_mapping_notify_event_t *e)
 void
 maprequest(xcb_map_request_event_t *e)
 {
-       struct ws_win           *win;
+       struct ws_win           *win, *w = NULL;
        xcb_get_window_attributes_reply_t *war;
 
        DNPRINTF(SWM_D_EVENT, "maprequest: win 0x%x\n",
@@ -8239,8 +8353,25 @@ maprequest(xcb_map_request_event_t *e)
            (war->map_state == XCB_MAP_STATE_VIEWABLE));
 
        /* The new window should get focus; prepare. */
-       if (focus_mode != SWM_FOCUS_FOLLOW)
-               win->ws->focus_pending = get_focus_magic(win);
+       if (focus_mode != SWM_FOCUS_FOLLOW &&
+           !(win->quirks & SWM_Q_NOFOCUSONMAP)) {
+               if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) {
+                       /* See if other wins of same type are already mapped. */
+                       TAILQ_FOREACH(w, &win->ws->winlist, entry) {
+                               if (w == win || !w->mapped)
+                                       continue;
+
+                               if (!strcmp(w->ch.class_name,
+                                   win->ch.class_name) &&
+                                   !strcmp(w->ch.instance_name,
+                                   win->ch.instance_name))
+                                       break;
+                       }
+               }
+
+               if (w == NULL)
+                       win->ws->focus_pending = get_focus_magic(win);
+       }
 
        /* All windows need to be mapped if they are in the current workspace.*/
        if (win->ws->r)
@@ -8680,6 +8811,7 @@ scan_xrandr(int i)
        int                                             ncrtc = 0;
 #endif /* SWM_XRR_HAS_CRTC */
        struct swm_region                               *r;
+       struct ws_win                                   *win;
        int                                             num_screens;
        xcb_randr_get_screen_resources_current_cookie_t src;
        xcb_randr_get_screen_resources_current_reply_t  *srr;
@@ -8753,6 +8885,16 @@ scan_xrandr(int i)
                    screen->height_in_pixels);
 
 out:
+       /* Cleanup unused previously visible workspaces. */
+       TAILQ_FOREACH(r, &screens[i].orl, entry) {
+               TAILQ_FOREACH(win, &r->ws->winlist, entry)
+                       unmap_window(win);
+
+               /* The screen shouldn't focus on an unused region. */
+               if (screens[i].r_focus == r)
+                       screens[i].r_focus = NULL;
+       }
+
        DNPRINTF(SWM_D_MISC, "scan_xrandr: done.\n");
 }
 
@@ -8779,10 +8921,22 @@ 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);
+       }
+
        stack();
+
+       /* Make sure a region has focus on each screen. */
+       for (i = 0; i < num_screens; i++) {
+               if (screens[i].r_focus == NULL) {
+                       r = TAILQ_FIRST(&screens[i].rl);
+                       if (r != NULL)
+                               focus_region(r);
+               }
+       }
+
        bar_draw();
        focus_flush();
 }
@@ -9262,6 +9416,8 @@ noconfig:
        if (cfile)
                conf_load(cfile, SWM_CONF_DEFAULT);
 
+       validate_spawns();
+
        setup_ewmh();
        /* set some values to work around bad programs */
        workaround();
@@ -9334,11 +9490,11 @@ noconfig:
 
                if (stdin_ready) {
                        stdin_ready = 0;
-                       if (bar_extra_update()) {
-                               bar_draw();
-                               xcb_flush(conn);
-                       }
+                       bar_extra_update();
                }
+
+               bar_draw();
+               xcb_flush(conn);
        }
 done:
        shutdown_cleanup();