JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix stacking issue at (re)start when spawn_position = first or prev.
[spectrwm.git] / spectrwm.c
index 822d1f5..db89878 100644 (file)
@@ -5,7 +5,7 @@
  * 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-2013 Reginald Kennedy <rk@rejii.com>
+ * Copyright (c) 2011-2014 Reginald Kennedy <rk@rejii.com>
  * Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
  * Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
  * Copyright (c) 2012-2013 David Hill <dhill@mindcry.org>
@@ -83,6 +83,7 @@
 #include <pwd.h>
 #include <regex.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -425,6 +426,7 @@ XftFont             *bar_font;
 int             bar_font_legacy = 1;
 char           *bar_fonts;
 XftColor        bar_font_color;
+XftColor        search_font_color;
 struct passwd  *pwd;
 char           *startup_exception;
 unsigned int    nr_exceptions = 0;
@@ -669,6 +671,9 @@ struct quirk {
 #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. */
+#define SWM_Q_OBEYAPPFOCUSREQ  (1<<8)  /* Focus when applications ask. */
+#define SWM_Q_IGNOREPID                (1<<9)  /* Ignore PID when determining ws. */
+#define SWM_Q_IGNORESPAWNWS    (1<<10) /* Ignore _SWM_WS when managing win. */
 };
 TAILQ_HEAD(quirk_list, quirk);
 struct quirk_list              quirks = TAILQ_HEAD_INITIALIZER(quirks);
@@ -1028,13 +1033,14 @@ int      get_region_index(struct swm_region *);
 xcb_screen_t   *get_screen(int);
 int     get_screen_count(void);
 #ifdef SWM_DEBUG
+char   *get_source_type_label(uint32_t);
 char   *get_stack_mode_name(uint8_t);
 #endif
 int32_t         get_swm_ws(xcb_window_t);
 char   *get_win_name(xcb_window_t);
 uint8_t         get_win_state(xcb_window_t);
 void    get_wm_protocols(struct ws_win *);
-int     get_ws_idx(xcb_window_t);
+int     get_ws_idx(struct ws_win *);
 void    grabbuttons(struct ws_win *);
 void    grabkeys(void);
 void    grab_windows(void);
@@ -1053,7 +1059,7 @@ void       kill_refs(struct ws_win *);
 void    leavenotify(xcb_leave_notify_event_t *);
 #endif
 void    load_float_geom(struct ws_win *);
-struct ws_win  *manage_window(xcb_window_t, int);
+struct ws_win  *manage_window(xcb_window_t, int, bool);
 void    map_window(struct ws_win *);
 void    mapnotify(xcb_map_notify_event_t *);
 void    mappingnotify(xcb_mapping_notify_event_t *);
@@ -1900,24 +1906,13 @@ name_to_pixel(int sidx, const char *colorname)
 void
 setscreencolor(const char *val, int i, int c)
 {
-       int     num_screens;
+       if (i < 0 || i >= get_screen_count())
+               return;
 
-       num_screens = get_screen_count();
-       if (i > 0 && i <= num_screens) {
-               screens[i - 1].c[c].pixel = name_to_pixel(i - 1, val);
-               free(screens[i - 1].c[c].name);
-               if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
-                       err(1, "strdup");
-       } else if (i == -1) {
-               for (i = 0; i < num_screens; i++) {
-                       screens[i].c[c].pixel = name_to_pixel(0, val);
-                       free(screens[i].c[c].name);
-                       if ((screens[i].c[c].name = strdup(val)) == NULL)
-                               err(1, "strdup");
-               }
-       } else
-               errx(1, "invalid screen index: %d out of bounds (maximum %d)",
-                   i, num_screens);
+       screens[i].c[c].pixel = name_to_pixel(i, val);
+       free(screens[i].c[c].name);
+       if ((screens[i].c[c].name = strdup(val)) == NULL)
+               err(1, "strdup");
 }
 
 void
@@ -2724,6 +2719,12 @@ xft_init(struct swm_region *r)
            DefaultColormap(display, r->s->idx), &color, &bar_font_color))
                warn("Xft error: unable to allocate color.");
 
+       PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color);
+
+       if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx),
+           DefaultColormap(display, r->s->idx), &color, &search_font_color))
+               warn("Xft error: unable to allocate color.");
+
        bar_height = bar_font->height + 2 * bar_border_width;
 
        if (bar_height < 1)
@@ -2771,6 +2772,13 @@ bar_setup(struct swm_region *r)
            XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL
            | XCB_CW_EVENT_MASK, wa);
 
+       /* Stack bar window above region window to start. */
+       wa[0] = r->id;
+       wa[1] = XCB_STACK_MODE_ABOVE;
+
+       xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING |
+           XCB_CONFIG_WINDOW_STACK_MODE, wa);
+
        r->bar->buffer = xcb_generate_id(conn);
        xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id,
            WIDTH(r->bar), HEIGHT(r->bar));
@@ -3485,7 +3493,7 @@ unfocus_win(struct ws_win *win)
 void
 focus_win(struct ws_win *win)
 {
-       struct ws_win                   *cfw = NULL, *parent = NULL, *w;
+       struct ws_win                   *cfw = NULL, *parent = NULL, *w, *tmpw;
        struct workspace                *ws;
        xcb_get_input_focus_reply_t     *gifr;
 
@@ -3568,20 +3576,25 @@ focus_win(struct ws_win *win)
                                map_window(parent);
 
                                /* Map siblings next. */
-                               TAILQ_FOREACH(w, &ws->winlist, entry)
+                               TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry,
+                                   tmpw)
                                        if (w != win && !ICONIC(w) &&
-                                           win->transient == parent->id)
+                                           w->transient == parent->id) {
+                                               raise_window(w);
                                                map_window(w);
+                                       }
                        }
 
                        /* Map focused window. */
                        raise_window(win);
                        map_window(win);
 
-                       /* Finally, map children of focus window. */
-                       TAILQ_FOREACH(w, &ws->winlist, entry)
-                               if (w->transient == win->id && !ICONIC(w))
+                       /* Stack any children of focus window. */
+                       TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw)
+                               if (w->transient == win->id && !ICONIC(w)) {
+                                       raise_window(w);
                                        map_window(w);
+                               }
                } else if (tile_gap < 0 && !ABOVE(win)) {
                        /*
                         * Windows overlap in the layout.
@@ -3593,13 +3606,15 @@ focus_win(struct ws_win *win)
 
                set_region(ws->r);
 
-               update_window_color(win);
-
                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
                    ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
                    &win->id);
        }
 
+       if (cfw != win)
+               /* Update window border even if workspace is hidden. */
+               update_window_color(win);
+
 out:
        bar_draw();
 
@@ -4231,17 +4246,21 @@ focus(struct swm_region *r, union arg *args)
        if (!(r && r->ws))
                goto out;
 
-       DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id);
-
        cur_focus = r->ws->focus;
        ws = r->ws;
        wl = &ws->winlist;
 
+       DNPRINTF(SWM_D_FOCUS, "focus: id: %d, cur_focus: %#x\n", args->id,
+           WINID(cur_focus));
+
        /* Make sure an uniconified window has focus, if one exists. */
        if (cur_focus == NULL) {
                cur_focus = TAILQ_FIRST(wl);
                while (cur_focus != NULL && ICONIC(cur_focus))
                        cur_focus = TAILQ_NEXT(cur_focus, entry);
+
+               DNPRINTF(SWM_D_FOCUS, "focus: new cur_focus: %#x\n",
+                   WINID(cur_focus));
        }
 
        switch (args->id) {
@@ -4256,8 +4275,10 @@ focus(struct swm_region *r, union arg *args)
                                winfocus = TAILQ_LAST(wl, ws_win_list);
                        if (winfocus == cur_focus)
                                break;
-               } while (winfocus != NULL &&
-                   (ICONIC(winfocus) || winfocus->id == cur_focus->transient));
+               } while (winfocus && (ICONIC(winfocus) ||
+                   winfocus->id == cur_focus->transient ||
+                   (cur_focus->transient != XCB_WINDOW_NONE &&
+                   winfocus->transient == cur_focus->transient)));
                break;
        case SWM_ARG_ID_FOCUSNEXT:
                if (cur_focus == NULL)
@@ -4270,8 +4291,10 @@ focus(struct swm_region *r, union arg *args)
                                winfocus = TAILQ_FIRST(wl);
                        if (winfocus == cur_focus)
                                break;
-               } while (winfocus != NULL &&
-                   (ICONIC(winfocus) || winfocus->id == cur_focus->transient));
+               } while (winfocus && (ICONIC(winfocus) ||
+                   winfocus->id == cur_focus->transient ||
+                   (cur_focus->transient != XCB_WINDOW_NONE &&
+                   winfocus->transient == cur_focus->transient)));
                break;
        case SWM_ARG_ID_FOCUSMAIN:
                if (cur_focus == NULL)
@@ -4493,6 +4516,8 @@ update_floater(struct ws_win *win)
 
        DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", win->id);
 
+       win->bordered = 1;
+
        if (FULLSCREEN(win)) {
                /* _NET_WM_FULLSCREEN: fullscreen without border. */
                if (!win->g_floatvalid)
@@ -4508,14 +4533,11 @@ update_floater(struct ws_win *win)
                win->g = r->g;
 
                if (bar_enabled && ws->bar_enabled) {
-                       win->bordered = 1;
                        if (!bar_at_bottom)
                                Y(win) += bar_height;
                        HEIGHT(win) -= bar_height;
                } else if (disable_border) {
                        win->bordered = 0;
-               } else {
-                       win->bordered = 1;
                }
 
                if (win->bordered) {
@@ -4898,7 +4920,7 @@ void
 max_stack(struct workspace *ws, struct swm_geometry *g)
 {
        struct swm_geometry     gg = *g;
-       struct ws_win           *w, *win = NULL, *parent = NULL;
+       struct ws_win           *w, *win = NULL, *parent = NULL, *tmpw;
        int                     winno;
 
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
@@ -4961,18 +4983,20 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                }
        }
 
-       if (TRANS(win)) {
-               parent = find_window(win->transient);
+       /* If transient, stack parent and its children. */
+       if (TRANS(win) && (parent = find_window(win->transient))) {
                raise_window(parent);
 
-               TAILQ_FOREACH(w, &ws->stack, stack_entry)
+               TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw)
                        if (w->transient == parent->id)
                                raise_window(w);
        }
 
+       /* Make sure focus window is on top. */
        raise_window(win);
 
-       TAILQ_FOREACH(w, &ws->stack, stack_entry)
+       /* Stack any children of focus window. */
+       TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw)
                if (w->transient == win->id)
                        raise_window(w);
 
@@ -5047,10 +5071,14 @@ send_to_ws(struct swm_region *r, union arg *args)
 
        win_to_ws(win, wsid, 1);
 
-       /* Set window to be focus on target ws. */
+       /* Set new focus on target ws. */
        if (focus_mode != SWM_FOCUS_FOLLOW) {
+               win->ws->focus_prev = win->ws->focus;
                win->ws->focus = win;
                win->ws->focus_pending = NULL;
+
+               if (win->ws->focus_prev)
+                       update_window_color(win->ws->focus_prev);
        }
 
        DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, "
@@ -5062,7 +5090,7 @@ send_to_ws(struct swm_region *r, union arg *args)
        ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED);
        ewmh_update_wm_state(win);
 
-       /* Restack and set new focus. */
+       /* Restack and set new focus on current ws. */
        if (FLOATING(win))
                load_float_geom(win);
 
@@ -5379,7 +5407,8 @@ search_win(struct swm_region *r, union arg *args)
        struct ws_win           *win = NULL;
        struct search_window    *sw = NULL;
        xcb_window_t            w;
-       uint32_t                wa[2];
+       uint32_t                wa[3];
+       xcb_screen_t            *screen;
        int                     i, width, height;
        char                    s[8];
        FILE                    *lfile;
@@ -5400,6 +5429,9 @@ search_win(struct swm_region *r, union arg *args)
        if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
                return;
 
+       if ((screen = get_screen(r->s->idx)) == NULL)
+               errx(1, "ERROR: can't get screen %d.", r->s->idx);
+
        TAILQ_INIT(&search_wl);
 
        i = 1;
@@ -5423,6 +5455,7 @@ search_win(struct swm_region *r, union arg *args)
                w = xcb_generate_id(conn);
                wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel;
                wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel;
+               wa[2] = screen->default_colormap;
 
                if (bar_font_legacy) {
                        XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox);
@@ -5435,10 +5468,10 @@ search_win(struct swm_region *r, union arg *args)
                        height = bar_font->height + 4;
                }
 
-               xcb_create_window(conn, XCB_COPY_FROM_PARENT, w, win->id, 0, 0,
+               xcb_create_window(conn, screen->root_depth, w, win->id, 0, 0,
                    width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
-                   XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL |
-                   XCB_CW_BORDER_PIXEL, wa);
+                   screen->root_visual, XCB_CW_BACK_PIXEL |
+                   XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa);
 
                xcb_map_window(conn, w);
 
@@ -5463,7 +5496,7 @@ search_win(struct swm_region *r, union arg *args)
                            DefaultVisual(display, r->s->idx),
                            DefaultColormap(display, r->s->idx));
 
-                       XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2,
+                       XftDrawStringUtf8(draw, &search_font_color, bar_font, 2,
                            (HEIGHT(r->bar) + bar_font->height) / 2 -
                            bar_font->descent, (FcChar8 *)s, len);
 
@@ -7519,6 +7552,9 @@ const char *quirkname[] = {
        "FOCUSPREV",
        "NOFOCUSONMAP",
        "FOCUSONMAP_SINGLE",
+       "OBEYAPPFOCUSREQ",
+       "IGNOREPID",
+       "IGNORESPAWNWS",
 };
 
 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
@@ -8102,34 +8138,40 @@ setconfmodkey(const char *selector, const char *value, int flags)
 int
 setconfcolor(const char *selector, const char *value, int flags)
 {
-       int     sid, i, num_screens;
+       int     first, last, i = 0, num_screens;
 
-       sid = (selector == NULL || strlen(selector) == 0) ? -1 : atoi(selector);
+       num_screens = get_screen_count();
 
-       /*
-        * When setting focus/unfocus colors, we need to also
-        * set maximize colors to match if they haven't been customized.
-        */
-       i = sid < 0 ? 0 : sid;
-       if (flags == SWM_S_COLOR_FOCUS &&
-           !screens[i].c[SWM_S_COLOR_FOCUS_MAXIMIZED].manual)
-               setscreencolor(value, sid, SWM_S_COLOR_FOCUS_MAXIMIZED);
-       else if (flags == SWM_S_COLOR_UNFOCUS &&
-           !screens[i].c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].manual)
-               setscreencolor(value, sid, SWM_S_COLOR_UNFOCUS_MAXIMIZED);
+       /* conf screen indices begin at 1; treat vals <= 0 as 'all screens.' */
+       if (selector == NULL || strlen(selector) == 0 ||
+           (last = atoi(selector) - 1) < 0) {
+               first = 0;
+               last = num_screens - 1;
+       } else {
+               first = last;
+       }
 
-       setscreencolor(value, sid, flags);
+       if (last >= num_screens) {
+               add_startup_exception("invalid screen index: %d out of bounds "
+                   "(maximum %d)", last + 1, num_screens);
+               return (1);
+       }
+
+       for (i = first; i <= last; ++i) {
+               setscreencolor(value, i, flags);
+
+               /*
+                * When setting focus/unfocus colors, we need to also
+                * set maximize colors to match if they haven't been customized.
+                */
+               if (flags == SWM_S_COLOR_FOCUS &&
+                   !screens[i].c[SWM_S_COLOR_FOCUS_MAXIMIZED].manual)
+                       setscreencolor(value, i, SWM_S_COLOR_FOCUS_MAXIMIZED);
+               else if (flags == SWM_S_COLOR_UNFOCUS &&
+                   !screens[i].c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].manual)
+                       setscreencolor(value, i, SWM_S_COLOR_UNFOCUS_MAXIMIZED);
 
-       /* Track override of color. */
-       num_screens = get_screen_count();
-       if (sid > 0 && sid <= num_screens) {
                screens[i].c[flags].manual = 1;
-       } else if (sid == -1) {
-               for (i = 0; i < num_screens; ++i)
-                       screens[i].c[flags].manual = 1;
-       } else {
-               errx(1, "invalid screen index: %d out of bounds (maximum %d)",
-                   sid, num_screens);
        }
 
        return (0);
@@ -8274,32 +8316,26 @@ setlayout(const char *selector, const char *value, int flags)
                        ws[ws_id].cur_layout->l_config(&ws[ws_id],
                            mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
                            SWM_ARG_ID_MASTERSHRINK);
-                       stack();
                }
                /* master add */
                for (x = 0; x < abs(ma); x++) {
                        ws[ws_id].cur_layout->l_config(&ws[ws_id],
                            ma >= 0 ?  SWM_ARG_ID_MASTERADD :
                            SWM_ARG_ID_MASTERDEL);
-                       stack();
                }
                /* stack inc */
                for (x = 0; x < abs(si); x++) {
                        ws[ws_id].cur_layout->l_config(&ws[ws_id],
                            si >= 0 ?  SWM_ARG_ID_STACKINC :
                            SWM_ARG_ID_STACKDEC);
-                       stack();
                }
                /* Apply flip */
                if (f) {
                        ws[ws_id].cur_layout->l_config(&ws[ws_id],
                            SWM_ARG_ID_FLIPLAYOUT);
-                       stack();
                }
        }
 
-       focus_flush();
-
        return (0);
 }
 
@@ -8641,13 +8677,16 @@ get_swm_ws(xcb_window_t id)
 }
 
 int
-get_ws_idx(xcb_window_t id)
+get_ws_idx(struct ws_win *win)
 {
        xcb_get_property_reply_t        *gpr;
        int                     ws_idx = -1;
 
+       if (win == NULL)
+               return -1;
+
        gpr = xcb_get_property_reply(conn,
-               xcb_get_property(conn, 0, id, ewmh[_NET_WM_DESKTOP].atom,
+               xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_DESKTOP].atom,
                    XCB_ATOM_CARDINAL, 0, 1),
                NULL);
        if (gpr) {
@@ -8656,20 +8695,20 @@ get_ws_idx(xcb_window_t id)
                free(gpr);
        }
 
-       if (ws_idx == -1)
-               if ((ws_idx = get_swm_ws(id)) != -1)
-                       xcb_delete_property(conn, id, a_swm_ws);
+       if (ws_idx == -1 && !(win->quirks & SWM_Q_IGNORESPAWNWS))
+               ws_idx = get_swm_ws(win->id);
 
        if (ws_idx > workspace_limit - 1 || ws_idx < -1)
                ws_idx = -1;
 
-       DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", id, ws_idx);
+       DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", win->id,
+           ws_idx);
 
        return ws_idx;
 }
 
 struct ws_win *
-manage_window(xcb_window_t id, int mapped)
+manage_window(xcb_window_t id, int spawn_pos, bool mapped)
 {
        struct ws_win           *win, *ww;
        struct swm_region       *r;
@@ -8762,32 +8801,6 @@ manage_window(xcb_window_t id, int mapped)
        /* Get WM_PROTOCOLS. */
        get_wm_protocols(win);
 
-       /* Figure out which workspace the window belongs to. */
-       if ((p = find_pid(window_get_pid(win->id))) != NULL) {
-               win->ws = &r->s->ws[p->ws];
-               TAILQ_REMOVE(&pidlist, p, entry);
-               free(p);
-               p = NULL;
-       } else if ((ws_idx = get_ws_idx(win->id)) != -1 &&
-           !TRANS(win)) {
-               /* _SWM_WS is set; use that. */
-               win->ws = &r->s->ws[ws_idx];
-       } else if (trans && (ww = find_window(trans)) != NULL) {
-               /* Launch transients in the same ws as parent. */
-               win->ws = ww->ws;
-       } else {
-               win->ws = r->ws;
-       }
-
-       /* Set the _NET_WM_DESKTOP atom. */
-       DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n",
-           win->ws->idx);
-       xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
-           ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx);
-
-       /* WS must already be set for this to work. */
-       store_float_geom(win);
-
        /* Set initial quirks based on EWMH. */
        ewmh_autoquirk(win);
 
@@ -8830,6 +8843,36 @@ manage_window(xcb_window_t id, int mapped)
                        fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT);
        }
 
+       /* Figure out which workspace the window belongs to. */
+       if (!(win->quirks & SWM_Q_IGNOREPID) &&
+           (p = find_pid(window_get_pid(win->id))) != NULL) {
+               win->ws = &r->s->ws[p->ws];
+               TAILQ_REMOVE(&pidlist, p, entry);
+               free(p);
+               p = NULL;
+       } else if ((ws_idx = get_ws_idx(win)) != -1 &&
+           !TRANS(win)) {
+               /* _SWM_WS is set; use that. */
+               win->ws = &r->s->ws[ws_idx];
+       } else if (trans && (ww = find_window(trans)) != NULL) {
+               /* Launch transients in the same ws as parent. */
+               win->ws = ww->ws;
+       } else {
+               win->ws = r->ws;
+       }
+
+       /* Set the _NET_WM_DESKTOP atom. */
+       DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n",
+           win->ws->idx);
+       xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
+           ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx);
+
+       /* Remove any _SWM_WS now that we set _NET_WM_DESKTOP. */
+       xcb_delete_property(conn, win->id, a_swm_ws);
+
+       /* WS must already be set for this to work. */
+       store_float_geom(win);
+
        /* Make sure window is positioned inside its region, if its active. */
        if (win->ws->r) {
                region_containment(win, r, SWM_CW_ALLSIDES |
@@ -8841,12 +8884,12 @@ out:
        /* Figure out where to stack the window in the workspace. */
        if (trans && (ww = find_window(trans)))
                TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
-       else if (win->ws->focus && spawn_position == SWM_STACK_ABOVE)
+       else if (win->ws->focus && spawn_pos == SWM_STACK_ABOVE)
                TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win,
                    entry);
-       else if (win->ws->focus && spawn_position == SWM_STACK_BELOW)
+       else if (win->ws->focus && spawn_pos == SWM_STACK_BELOW)
                TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
-       else switch (spawn_position) {
+       else switch (spawn_pos) {
        default:
        case SWM_STACK_TOP:
        case SWM_STACK_ABOVE:
@@ -9426,6 +9469,13 @@ enternotify(xcb_enter_notify_event_t *e)
                        return;
                }
        } else {
+               if (e->mode == XCB_NOTIFY_MODE_NORMAL &&
+                   e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
+                       DNPRINTF(SWM_D_EVENT, "enternotify: entering from "
+                           "inferior; ignoring\n");
+                       return;
+               }
+
                focus_win(get_focus_magic(win));
        }
 
@@ -9455,7 +9505,7 @@ mapnotify(xcb_map_notify_event_t *e)
 
        DNPRINTF(SWM_D_EVENT, "mapnotify: win %#x\n", e->window);
 
-       if ((win = manage_window(e->window, 1)) == NULL)
+       if ((win = manage_window(e->window, spawn_position, true)) == NULL)
                return;
        ws = win->ws;
 
@@ -9528,7 +9578,7 @@ maprequest(xcb_map_request_event_t *e)
                goto out;
        }
 
-       win = manage_window(e->window,
+       win = manage_window(e->window, spawn_position,
            (war->map_state == XCB_MAP_STATE_VIEWABLE));
        if (win == NULL)
                goto out;
@@ -9823,7 +9873,8 @@ clientmessage(xcb_client_message_event_t *e)
                 * Allow focus changes that are a result of direct user
                 * action and from applications that use the old EWMH spec.
                 */
-               if (e->data.data32[0] != EWMH_SOURCE_TYPE_NORMAL) {
+               if (e->data.data32[0] != EWMH_SOURCE_TYPE_NORMAL ||
+                   win->quirks & SWM_Q_OBEYAPPFOCUSREQ) {
                        if (WS_FOCUSED(win->ws))
                                focus_win(win);
                        else
@@ -10289,7 +10340,7 @@ grab_windows(void)
                        manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
                        mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE;
                        if (mapped || manage)
-                               manage_window(wins[j], mapped);
+                               manage_window(wins[j], SWM_STACK_TOP, mapped);
                        free(gar);
                }
                /* transient windows */
@@ -10316,7 +10367,7 @@ grab_windows(void)
                        pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
                        if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
                            &trans, NULL) && manage)
-                               manage_window(wins[j], mapped);
+                               manage_window(wins[j], SWM_STACK_TOP, mapped);
                        free(gar);
                }
                free(qtr);
@@ -10371,15 +10422,16 @@ setup_screens(void)
                screens[i].root = screen->root;
 
                /* set default colors */
-               setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
-               setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
-               setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
-               setscreencolor("rgb:00/40/40", i + 1,
+               setscreencolor("red", i, SWM_S_COLOR_FOCUS);
+               setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS);
+               setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER);
+               setscreencolor("rgb:00/40/40", i,
                    SWM_S_COLOR_BAR_BORDER_UNFOCUS);
-               setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
-               setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
-               setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS_MAXIMIZED);
-               setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS_MAXIMIZED);
+               setscreencolor("black", i, SWM_S_COLOR_BAR);
+               setscreencolor("rgb:a0/a0/a0", i, SWM_S_COLOR_BAR_FONT);
+               setscreencolor("red", i, SWM_S_COLOR_FOCUS_MAXIMIZED);
+               setscreencolor("rgb:88/88/88", i,
+                   SWM_S_COLOR_UNFOCUS_MAXIMIZED);
 
                /* create graphics context on screen */
                screens[i].bar_gc = xcb_generate_id(conn);
@@ -10470,9 +10522,12 @@ shutdown_cleanup(void)
 
                if (screens[i].bar_gc != XCB_NONE)
                        xcb_free_gc(conn, screens[i].bar_gc);
-               if (!bar_font_legacy)
+               if (!bar_font_legacy) {
                        XftColorFree(display, DefaultVisual(display, i),
                            DefaultColormap(display, i), &bar_font_color);
+                       XftColorFree(display, DefaultVisual(display, i),
+                           DefaultColormap(display, i), &search_font_color);
+               }
        }
 
        if (bar_font_legacy)
@@ -10559,14 +10614,14 @@ main(int argc, char *argv[])
        struct swm_region       *r;
        char                    conf[PATH_MAX], *cfile = NULL;
        struct stat             sb;
-       int                     xfd, i, num_screens, startup = 1;
+       int                     xfd, i, num_screens;
        struct sigaction        sact;
        xcb_generic_event_t     *evt;
        struct timeval          tv;
        fd_set                  rd;
        int                     rd_max;
-       int                     stdin_ready = 0;
        int                     num_readable;
+       bool                    stdin_ready = false, startup = true;
 
        /* suppress unused warning since var is needed */
        (void)argc;
@@ -10712,7 +10767,7 @@ noconfig:
 
                /* If just (re)started, set default focus if needed. */
                if (startup) {
-                       startup = 0;
+                       startup = false;
 
                        if (focus_mode != SWM_FOCUS_FOLLOW) {
                                r = TAILQ_FIRST(&screens[0].rl);
@@ -10736,7 +10791,7 @@ noconfig:
                if (num_readable == -1 && errno != EINTR) {
                        DNPRINTF(SWM_D_MISC, "select failed");
                } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) {
-                       stdin_ready = 1;
+                       stdin_ready = true;
                }
 
                if (restart_wm)
@@ -10749,7 +10804,7 @@ noconfig:
                        goto done;
 
                if (stdin_ready) {
-                       stdin_ready = 0;
+                       stdin_ready = false;
                        bar_extra_update();
                }