JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
missing -
[spectrwm.git] / scrotwm.c
index 6964df7..cbba249 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -96,8 +96,8 @@ static const char     *cvstag = "$scrotwm$";
 
 /* #define SWM_DEBUG */
 #ifdef SWM_DEBUG
-#define DPRINTF(x...)          do { if (swm_debug) fprintf(stderr, x); } while(0)
-#define DNPRINTF(n,x...)       do { if (swm_debug & n) fprintf(stderr, x); } while(0)
+#define DPRINTF(x...)          do { if (swm_debug) fprintf(stderr, x); } while (0)
+#define DNPRINTF(n,x...)       do { if (swm_debug & n) fprintf(stderr, x); } while (0)
 #define        SWM_D_MISC              0x0001
 #define        SWM_D_EVENT             0x0002
 #define        SWM_D_WS                0x0004
@@ -148,6 +148,8 @@ u_int32_t           swm_debug = 0
 #define WIDTH(r)               (r)->g.w
 #define HEIGHT(r)              (r)->g.h
 #define SWM_MAX_FONT_STEPS     (3)
+#define SWM_EV_PROLOGUE(x)     do { XGrabServer(x); } while (0)
+#define SWM_EV_EPILOGUE(x)     do { XUngrabServer(x); XFlush(x); } while (0)
 
 #ifndef SWM_LIB
 #define SWM_LIB                        "/usr/local/lib/libswmhack.so"
@@ -190,6 +192,7 @@ int                 bar_extra = 1;
 int                    bar_extra_running = 0;
 int                    bar_verbose = 1;
 int                    bar_height = 0;
+int                    stack_enabled = 1;
 int                    clock_enabled = 1;
 int                    title_name_enabled = 0;
 int                    title_class_enabled = 0;
@@ -319,12 +322,17 @@ void      new_region(struct swm_screen *, int, int, int, int);
 struct layout {
        void            (*l_stack)(struct workspace *, struct swm_geometry *);
        void            (*l_config)(struct workspace *, int);
+       u_int32_t       flags;
+#define SWM_L_FOCUSPREV                (1<<0)
+#define SWM_L_MAPONFOCUS       (1<<1)
+       char            *name;
 } layouts[] =  {
        /* stack,               configure */
-       { vertical_stack,       vertical_config},
-       { horizontal_stack,     horizontal_config},
-       { max_stack,            NULL},
-       { NULL,                 NULL},
+       { vertical_stack,       vertical_config,        0,      "[|]" },
+       { horizontal_stack,     horizontal_config,      0,      "[-]" },
+       { max_stack,            NULL,
+         SWM_L_FOCUSPREV | SWM_L_MAPONFOCUS,                   "[ ]"},
+       { NULL,                 NULL,                   0},
 };
 
 #define SWM_H_SLICE            (32)
@@ -753,6 +761,7 @@ bar_update(void)
        char                    s[SWM_BAR_MAX];
        char                    loc[SWM_BAR_MAX];
        char                    *b;
+       char                    *stack = "";
 
        if (bar_enabled == 0)
                return;
@@ -785,12 +794,15 @@ bar_update(void)
                        if (r && r->ws)
                                bar_class_name(s, sizeof s, r->ws->focus);
 
-                       snprintf(loc, sizeof loc, "%d:%d    %s %s    %s",
-                           x++, r->ws->idx + 1, s, bar_ext, bar_vertext);
+                       if (stack_enabled)
+                               stack = r->ws->cur_layout->name;
+
+                       snprintf(loc, sizeof loc, "%d:%d %s   %s %s    %s",
+                           x++, r->ws->idx + 1, stack, s, bar_ext,
+                           bar_vertext);
                        bar_print(r, loc);
                }
        }
-       XSync(display, False);
        alarm(bar_delay);
 }
 
@@ -1222,6 +1234,8 @@ focus_win(struct ws_win *win)
                XSetWindowBorder(display, win->id,
                    win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
                grabbuttons(win, 1);
+               if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS)
+                       XMapRaised(display, win->id);
                XSetInputFocus(display, win->id,
                    RevertToPointerRoot, CurrentTime);
                XSync(display, False);
@@ -1330,6 +1344,10 @@ cyclescr(struct swm_region *r, union arg *args)
        struct swm_region       *rr;
        int                     i;
 
+       /* do nothing if we don't have more than one screen */
+       if (!(ScreenCount(display) > 1 || outputs > 1))
+               return;
+
        i = r->s->idx;
        switch (args->id) {
        case SWM_ARG_ID_CYCLESC_UP:
@@ -1347,8 +1365,8 @@ cyclescr(struct swm_region *r, union arg *args)
        };
        unfocus_all();
        XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime);
-       XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, rr->g.x,
-           rr->g.y + bar_enabled ? bar_height : 0);
+       XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, rr->g.x + 1,
+           rr->g.y + bar_enabled + 1 ? bar_height : 0);
 }
 
 void
@@ -1457,9 +1475,7 @@ focus(struct swm_region *r, union arg *args)
        if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
-       XMapRaised(display, winfocus->id);
        focus_win(winfocus);
-       XSync(display, False);
 }
 
 void
@@ -1479,6 +1495,7 @@ cycle_layout(struct swm_region *r, union arg *args)
        ignore_enter = 1;
        stack();
        focus_win(winfocus);
+       ignore_enter = 0;
 }
 
 void
@@ -1871,6 +1888,9 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
 
+       if (ws == NULL)
+               return;
+
        winno = count_win(ws, 0);
        if (winno == 0 && count_win(ws, 1) == 0)
                return;
@@ -1887,15 +1907,17 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                        win->g.h = wc.height = gg.h;
                        mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
                        XConfigureWindow(display, win->id, mask, &wc);
+
+                       /* unmap only if we don't have multi screen */
                        if (win != ws->focus)
-                               unmap_window(win);
+                               if (!(ScreenCount(display) > 1 || outputs > 1))
+                                       unmap_window(win);
                }
        }
 
        /* put the last transient on top */
        if (wintrans) {
                stack_floater(wintrans, ws->r);
-               XMapRaised(display, wintrans->id);
                focus_win(wintrans); /* override */
        }
 }
@@ -1904,11 +1926,15 @@ void
 send_to_ws(struct swm_region *r, union arg *args)
 {
        int                     wsid = args->id;
-       struct ws_win           *win = r->ws->focus, *winfocus = NULL;
+       struct ws_win           *win = win, *winfocus = NULL;
        struct workspace        *ws, *nws;
        Atom                    ws_idx_atom = 0;
        unsigned char           ws_idx_str[SWM_PROPLEN];
 
+       if (r && r->ws)
+               win = r->ws->focus;
+       else
+               return;
        if (win == NULL)
                return;
 
@@ -1922,18 +1948,18 @@ send_to_ws(struct swm_region *r, union arg *args)
        if (TAILQ_FIRST(&ws->winlist) == win)
                winfocus = TAILQ_NEXT(win, entry);
        else {
-               winfocus = TAILQ_PREV(ws->focus, ws_win_list, entry);
+               winfocus = TAILQ_PREV(win, ws_win_list, entry);
                if (winfocus == NULL)
                        winfocus = TAILQ_LAST(&ws->winlist, ws_win_list);
        }
-       /* out of windows in ws so focus on nws instead */
+       /* out of windows in ws so focus on nws instead if we multi screen */
        if (winfocus == NULL)
-               winfocus = win;
+               if (ScreenCount(display) > 1 || outputs > 1)
+                       winfocus = win;
 
-       unmap_window(win);
 
+       unmap_window(win);
        TAILQ_REMOVE(&ws->winlist, win, entry);
-
        TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
        win->ws = nws;
 
@@ -1953,7 +1979,8 @@ send_to_ws(struct swm_region *r, union arg *args)
        nws->restack = 1;
 
        stack();
-       focus_win(winfocus);
+       if (winfocus)
+               focus_win(winfocus);
 }
 
 void
@@ -2141,8 +2168,15 @@ move(struct ws_win *win, union arg *args)
 }
 
 /* key definitions */
-void dummykeyfunc(struct swm_region *r, union arg *args) {};
-void legacyfunc(struct swm_region *r, union arg *args) {};
+void
+dummykeyfunc(struct swm_region *r, union arg *args)
+{
+};
+
+void
+legacyfunc(struct swm_region *r, union arg *args)
+{
+};
 
 struct keyfunc {
        char                    name[SWM_FUNCNAME_LEN];
@@ -2788,65 +2822,6 @@ grabbuttons(struct ws_win *win, int focused)
                    BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
 }
 
-void
-expose(XEvent *e)
-{
-       DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
-}
-
-void
-keypress(XEvent *e)
-{
-       unsigned int            i;
-       KeySym                  keysym;
-       XKeyEvent               *ev = &e->xkey;
-
-       DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
-
-       keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
-       for (i = 0; i < keys_length; i++)
-               if (keysym == keys[i].keysym
-                  && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
-                  && keyfuncs[keys[i].funcid].func) {
-                       if (keys[i].funcid == kf_spawn_custom)
-                               spawn_custom(
-                                   root_to_region(ev->root),
-                                   &(keyfuncs[keys[i].funcid].args),
-                                   keys[i].spawn_name
-                                   );
-                       else
-                               keyfuncs[keys[i].funcid].func(
-                                   root_to_region(ev->root),
-                                   &(keyfuncs[keys[i].funcid].args)
-                                   );
-               }
-}
-
-void
-buttonpress(XEvent *e)
-{
-       XButtonPressedEvent     *ev = &e->xbutton;
-
-       struct ws_win           *win;
-       int                     i, action;
-
-       DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
-
-       action = root_click;
-       if ((win = find_window(ev->window)) == NULL)
-               return;
-       else {
-               focus_win(win);
-               action = client_click;
-       }
-
-       for (i = 0; i < LENGTH(buttons); i++)
-               if (action == buttons[i].action && buttons[i].func &&
-                   buttons[i].button == ev->button &&
-                   CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
-                       buttons[i].func(win, &buttons[i].args);
-}
-
 const char *quirkname[] = {
        "NONE",         /* config string for "no value" */
        "FLOAT",
@@ -2863,8 +2838,10 @@ parsequirks(char *qstr, unsigned long *quirk)
 {
        char                    *cp, *name;
        int                     i;
+
        if (quirk == NULL)
                return (1);
+
        cp = qstr;
        *quirk = 0;
        while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
@@ -2893,6 +2870,7 @@ void
 setquirk(const char *class, const char *name, const int quirk)
 {
        int                     i, j;
+
        /* find existing */
        for (i = 0; i < quirks_length; i++) {
                if (!strcmp(quirks[i].class, class) &&
@@ -3001,11 +2979,11 @@ setup_quirks(void)
 /* conf file stuff */
 #define SWM_CONF_FILE  "scrotwm.conf"
 
-enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_CLOCK_ENABLED,
-         SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
-         SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED, SWM_S_TITLE_NAME_ENABLED,
-         SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP,
-         SWM_S_DIALOG_RATIO };
+enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_STACK_ENABLED,
+         SWM_S_CLOCK_ENABLED, SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE,
+         SWM_S_SS_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
+         SWM_S_TITLE_NAME_ENABLED, SWM_S_BAR_FONT, SWM_S_BAR_ACTION,
+         SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO };
 
 int
 setconfvalue(char *selector, char *value, int flags)
@@ -3017,6 +2995,9 @@ setconfvalue(char *selector, char *value, int flags)
        case SWM_S_BAR_ENABLED:
                bar_enabled = atoi(value);
                break;
+       case SWM_S_STACK_ENABLED:
+               stack_enabled = atoi(value);
+               break;
        case SWM_S_CLOCK_ENABLED:
                clock_enabled = atoi(value);
                break;
@@ -3111,6 +3092,7 @@ struct config_option configopt[] = {
        { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
        { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
        { "bind",                       setconfbinding, 0 },
+       { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
        { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
        { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
        { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
@@ -3401,6 +3383,65 @@ unmanage_window(struct ws_win *win)
 }
 
 void
+expose(XEvent *e)
+{
+       DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
+}
+
+void
+keypress(XEvent *e)
+{
+       unsigned int            i;
+       KeySym                  keysym;
+       XKeyEvent               *ev = &e->xkey;
+
+       DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
+
+       keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
+       for (i = 0; i < keys_length; i++)
+               if (keysym == keys[i].keysym
+                  && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+                  && keyfuncs[keys[i].funcid].func) {
+                       if (keys[i].funcid == kf_spawn_custom)
+                               spawn_custom(
+                                   root_to_region(ev->root),
+                                   &(keyfuncs[keys[i].funcid].args),
+                                   keys[i].spawn_name
+                                   );
+                       else
+                               keyfuncs[keys[i].funcid].func(
+                                   root_to_region(ev->root),
+                                   &(keyfuncs[keys[i].funcid].args)
+                                   );
+               }
+}
+
+void
+buttonpress(XEvent *e)
+{
+       XButtonPressedEvent     *ev = &e->xbutton;
+
+       struct ws_win           *win;
+       int                     i, action;
+
+       DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
+
+       action = root_click;
+       if ((win = find_window(ev->window)) == NULL)
+               return;
+       else {
+               focus_win(win);
+               action = client_click;
+       }
+
+       for (i = 0; i < LENGTH(buttons); i++)
+               if (action == buttons[i].action && buttons[i].func &&
+                   buttons[i].button == ev->button &&
+                   CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+                       buttons[i].func(win, &buttons[i].args);
+}
+
+void
 configurerequest(XEvent *e)
 {
        XConfigureRequestEvent  *ev = &e->xconfigurerequest;
@@ -3490,17 +3531,33 @@ destroynotify(XEvent *e)
 
        DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
 
+       SWM_EV_PROLOGUE(display);
+
        if ((win = find_window(ev->window)) != NULL) {
                /* find a window to focus */
                ws = win->ws;
                wl = &ws->winlist;
-               if (ws->focus == win) {
-                       if (TAILQ_FIRST(wl) == win)
-                               winfocus = TAILQ_NEXT(win, entry);
-                       else {
-                               winfocus = TAILQ_PREV(ws->focus, ws_win_list, entry);
-                               if (winfocus == NULL)
-                                       winfocus = TAILQ_LAST(wl, ws_win_list);
+
+               /* if we are transient give focus to parent */
+               if (win->transient)
+                       winfocus = find_window(win->transient);
+               else if (ws->focus == win) {
+                       /* if in max_stack try harder */
+                       if (ws->cur_layout->flags & SWM_L_FOCUSPREV)
+                               if (win != ws->focus && win != ws->focus_prev)
+                                       winfocus = ws->focus_prev;
+
+                       /* fallback and normal handling */
+                       if (winfocus == NULL) {
+                               if (TAILQ_FIRST(wl) == win)
+                                       winfocus = TAILQ_NEXT(win, entry);
+                               else {
+                                       winfocus = TAILQ_PREV(ws->focus,
+                                           ws_win_list, entry);
+                                       if (winfocus == NULL)
+                                               winfocus = TAILQ_LAST(wl,
+                                                   ws_win_list);
+                               }
                        }
                }
 
@@ -3509,6 +3566,8 @@ destroynotify(XEvent *e)
                if (winfocus)
                        focus_win(winfocus);
        }
+
+       SWM_EV_EPILOGUE(display);
 }
 
 void
@@ -3555,6 +3614,8 @@ mapnotify(XEvent *e)
 
        DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window);
 
+       SWM_EV_PROLOGUE(display);
+
        win = find_window(ev->window);
        if (win)
                set_win_state(win, NormalState);
@@ -3562,6 +3623,8 @@ mapnotify(XEvent *e)
        XRefreshKeyboardMapping(ev);
        if (ev->request == MappingKeyboard)
                grabkeys();
+
+       SWM_EV_EPILOGUE(display);
 }
 
 void
@@ -3576,10 +3639,12 @@ maprequest(XEvent *e)
        DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
            e->xmaprequest.window);
 
+       SWM_EV_PROLOGUE(display);
+
        if (!XGetWindowAttributes(display, ev->window, &wa))
-               return;
+               goto done;
        if (wa.override_redirect)
-               return;
+               goto done;
 
        manage_window(e->xmaprequest.window);
 
@@ -3591,6 +3656,9 @@ maprequest(XEvent *e)
 
        if (win->ws == r->ws)
                focus_win(win);
+
+done:
+       SWM_EV_EPILOGUE(display);
 }
 
 void
@@ -3631,17 +3699,19 @@ propertynotify(XEvent *e)
 void
 unmapnotify(XEvent *e)
 {
-       struct ws_win           *win, *winfocus;
+       struct ws_win           *win, *winfocus = NULL;
        struct workspace        *ws;
 
        DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
 
+       SWM_EV_PROLOGUE(display);
+
        /* determine if we need to help unmanage this window */
        win = find_window(e->xunmap.window);
        if (win == NULL)
-               return;
+               goto done;
        if (win->transient)
-               return;
+               goto done;
 
        if (getstate(e->xunmap.window) == NormalState) {
                /*
@@ -3649,15 +3719,24 @@ unmapnotify(XEvent *e)
                 * longer visible due to the app unmapping it so unmanage it
                 */
 
-               /* find something to focus */
                ws = win->ws;
-               winfocus = TAILQ_PREV(win, ws_win_list, entry);
-               if (TAILQ_FIRST(&ws->winlist) == win)
-                       winfocus = TAILQ_NEXT(win, entry);
-               else {
-                       winfocus = TAILQ_PREV(ws->focus, ws_win_list, entry);
-                       if (winfocus == NULL)
-                               winfocus = TAILQ_LAST(&ws->winlist, ws_win_list);
+               /* if we are max_stack try harder to focus on something */
+               if (ws->cur_layout->flags & SWM_L_FOCUSPREV)
+                       if (win != ws->focus && win != ws->focus_prev)
+                               winfocus = ws->focus_prev;
+
+               /* normal and fallback if haven't found anything to focus on */
+               if (winfocus == NULL) {
+                       winfocus = TAILQ_PREV(win, ws_win_list, entry);
+                       if (TAILQ_FIRST(&ws->winlist) == win)
+                               winfocus = TAILQ_NEXT(win, entry);
+                       else {
+                               winfocus = TAILQ_PREV(ws->focus, ws_win_list,
+                                   entry);
+                               if (winfocus == NULL)
+                                       winfocus = TAILQ_LAST(&ws->winlist,
+                                           ws_win_list);
+                       }
                }
 
                /* trash window and refocus */
@@ -3665,6 +3744,9 @@ unmapnotify(XEvent *e)
                stack();
                focus_win(winfocus);
        }
+
+done:
+       SWM_EV_EPILOGUE(display);
 }
 
 void
@@ -3847,7 +3929,6 @@ void
 screenchange(XEvent *e) {
        XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
        struct swm_region               *r;
-       struct ws_win                   *win;
        int                             i;
 
        DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
@@ -3865,11 +3946,6 @@ screenchange(XEvent *e) {
        /* brute force for now, just re-enumerate the regions */
        scan_xrandr(i);
 
-       /* hide any windows that went away */
-       TAILQ_FOREACH(r, &screens[i].rl, entry)
-               TAILQ_FOREACH(win, &r->ws->winlist, entry)
-                       unmap_window(win);
-
        /* add bars to all regions */
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)