JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
KNF
[spectrwm.git] / scrotwm.c
index 40a7fa3..0e3ab48 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -48,7 +48,9 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#define        SWM_VERSION     "0.5"
+static const char      *cvstag = "$scrotwm$";
+
+#define        SWM_VERSION     "0.6"
 
 #include <stdio.h>
 #include <stdlib.h>
 #define        SWM_D_MOUSE             0x0040
 #define        SWM_D_PROP              0x0080
 
-
 u_int32_t              swm_debug = 0
                            | SWM_D_MISC
                            | SWM_D_EVENT
@@ -111,6 +112,7 @@ u_int32_t           swm_debug = 0
                            | SWM_D_FOCUS
                            | SWM_D_MOVE
                            | SWM_D_STACK
+                           | SWM_D_MOUSE
                            | SWM_D_PROP
                            ;
 #else
@@ -122,18 +124,16 @@ u_int32_t         swm_debug = 0
 #define MODKEY                 Mod1Mask
 #define CLEANMASK(mask)                (mask & ~(numlockmask | LockMask))
 #define BUTTONMASK             (ButtonPressMask|ButtonReleaseMask)
-
-#define X(r)           (r)->g.x        
-#define Y(r)           (r)->g.y
-#define WIDTH(r)       (r)->g.w        
-#define HEIGHT(r)      (r)->g.h
+#define SWM_PROPLEN            (16)
+#define X(r)                   (r)->g.x        
+#define Y(r)                   (r)->g.y
+#define WIDTH(r)               (r)->g.w        
+#define HEIGHT(r)              (r)->g.h
 
 #ifndef SWM_LIB
-#define SWM_LIB                "/usr/X11R6/lib/swmhack.so"
+#define SWM_LIB                        "/usr/X11R6/lib/swmhack.so"
 #endif
 
-#define SWM_PROPLEN    (16)
-
 char                   **start_argv;
 Atom                   astate;
 int                    (*xerrorxlib)(Display *, XErrorEvent *);
@@ -175,9 +175,9 @@ char                        *bar_fonts[] = {
 
 /* terminal + args */
 char                   *spawn_term[] = { "xterm", NULL };
-char                   *spawn_screenshot[] = { "scrot", NULL, NULL }; /* XXX get from conf */
-char                   *spawn_menu[] = { "dmenu_run", "-fn", NULL,
-                           "-nb", NULL, "-nf", NULL, "-sb", NULL, "-sf", NULL, NULL };
+char                   *spawn_screenshot[] = { "screenshot.sh", NULL, NULL };
+char                   *spawn_menu[] = { "dmenu_run", "-fn", NULL, "-nb", NULL,
+                           "-nf", NULL, "-sb", NULL, "-sf", NULL, NULL };
 
 #define SWM_MENU_FN    (2)
 #define SWM_MENU_NB    (4)
@@ -216,6 +216,7 @@ struct ws_win {
        struct workspace        *ws;    /* always valid */
        struct swm_screen       *s;     /* always valid, never changes */
        XWindowAttributes       wa;
+       XSizeHints              sh;
 };
 TAILQ_HEAD(ws_win_list, ws_win);
 
@@ -301,6 +302,8 @@ union arg {
 #define SWM_ARG_ID_STACKINIT   (11)
 #define SWM_ARG_ID_CYCLEWS_UP  (12)
 #define SWM_ARG_ID_CYCLEWS_DOWN        (13)
+#define SWM_ARG_ID_CYCLESC_UP  (14)
+#define SWM_ARG_ID_CYCLESC_DOWN        (15)
 #define SWM_ARG_ID_SS_ALL      (0)
 #define SWM_ARG_ID_SS_WINDOW   (1)
        char                    **argv;
@@ -456,10 +459,13 @@ conf_load(char *filename)
 
                case 's':
                        if (!strncmp(var, "spawn_term", strlen("spawn_term")))
-                               asprintf(&spawn_term[0], "%s", val); /* XXX args? */
+                               asprintf(&spawn_term[0], "%s", val);
                        if (!strncmp(var, "screenshot_enabled",
                            strlen("screenshot_enabled")))
                                ss_enabled = atoi(val);
+                       if (!strncmp(var, "screenshot_app",
+                           strlen("screenshot_app")))
+                               asprintf(&spawn_screenshot[0], "%s", val);
                        break;
                default:
                        goto bad;
@@ -719,6 +725,18 @@ quit(struct swm_region *r, union arg *args)
 }
 
 void
+unmap_all(void)
+{
+       struct ws_win           *win;
+       int                     i, j;
+
+       for (i = 0; i < ScreenCount(display); i++)
+               for (j = 0; j < SWM_WS_MAX; j++)
+                       TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+                               XUnmapWindow(display, win->id);
+}
+
+void
 restart(struct swm_region *r, union arg *args)
 {
        DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
@@ -730,6 +748,7 @@ restart(struct swm_region *r, union arg *args)
 
        bar_extra_stop();
        bar_extra = 1;
+       unmap_all();
        XCloseDisplay(display);
        execvp(start_argv[0], start_argv);
        fprintf(stderr, "execvp failed\n");
@@ -784,6 +803,9 @@ find_window(Window id)
 void
 spawn(struct swm_region *r, union arg *args)
 {
+       char                    *ret;
+       int                     si;
+
        DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
        /*
         * The double-fork construct avoids zombie processes and keeps the code
@@ -791,15 +813,24 @@ spawn(struct swm_region *r, union arg *args)
         */
        if (fork() == 0) {
                if (fork() == 0) {
-                       char *ret;
                        if (display)
                                close(ConnectionNumber(display));
                        setenv("LD_PRELOAD", SWM_LIB, 1);
-                       if (asprintf(&ret, "%d", r->ws->idx))
+                       if (asprintf(&ret, "%d", r->ws->idx)) {
                                setenv("_SWM_WS", ret, 1);
-                       if (asprintf(&ret, "%d", getpid()))
+                               free(ret);
+                       }
+                       if (asprintf(&ret, "%d", getpid())) {
                                setenv("_SWM_PID", ret, 1);
+                               free(ret);
+                       }
                        setsid();
+                       /* kill stdin, mplayer, ssh-add etc. need that */
+                       si = open("/dev/null", O_RDONLY, 0);
+                       if (si == -1)
+                               err(1, "open /dev/null");
+                       if (dup2(si, 0) == -1)
+                               err(1, "dup2 /dev/null");
                        execvp(args->argv[0], args->argv);
                        fprintf(stderr, "execvp failed\n");
                        perror(" failed");
@@ -896,7 +927,6 @@ switchws(struct swm_region *r, union arg *args)
 
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
                        XUnmapWindow(display, win->id);
-
                old_ws->r = NULL;
                old_ws->restack = 1;
        } else {
@@ -925,7 +955,6 @@ cyclews(struct swm_region *r, union arg *args)
            r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
 
        a.id = r->ws->idx;
-
        do {
                switch (args->id) {
                case SWM_ARG_ID_CYCLEWS_UP:
@@ -954,6 +983,33 @@ cyclews(struct swm_region *r, union arg *args)
 }
 
 void
+cyclescr(struct swm_region *r, union arg *args)
+{
+       struct swm_region       *rr;
+       int                     i;
+
+       i = r->s->idx;
+       switch (args->id) {
+       case SWM_ARG_ID_CYCLESC_UP:
+               rr = TAILQ_NEXT(r, entry);
+               if (rr == NULL)
+                       rr = TAILQ_FIRST(&screens[i].rl);
+               break;
+       case SWM_ARG_ID_CYCLESC_DOWN:
+               rr = TAILQ_PREV(r, swm_region_list, entry);
+               if (rr == NULL)
+                       rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
+               break;
+       default:
+               return;
+       };
+       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);
+}
+
+void
 swapwin(struct swm_region *r, union arg *args)
 {
        struct ws_win           *target;
@@ -1038,7 +1094,7 @@ focus(struct swm_region *r, union arg *args)
                return;
        }
 
-       if (winfocus == winlostfocus)
+       if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
        XMapRaised(display, winfocus->id);
@@ -1057,7 +1113,6 @@ cycle_layout(struct swm_region *r, union arg *args)
        if (ws->cur_layout->l_stack == NULL)
                ws->cur_layout = &layouts[0];
        ignore_enter = 1;
-
        stack();
 }
 
@@ -1134,15 +1189,17 @@ stack_floater(struct ws_win *win, struct swm_region *r)
        int tmp;                                        \
        tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
        tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp;    \
-} while (0);
+} while (0)
 void
 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
 {
        XWindowChanges          wc;
        struct swm_geometry     win_g, r_g = *g;
        struct ws_win           *win, *winfocus;
-       int                     i, j;
-       int                     split, colno, hrh, winno, mwin, msize, mscale;
+       int                     i, j, w_inc, h_inc, w_base, h_base;
+       int                     hrh, extra, h_slice, last_h = 0;
+       int                     split, colno, winno, mwin, msize, mscale;
+       int                     remain, missing, v_slice;;
        unsigned int            mask;
 
        DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s",
@@ -1155,26 +1212,52 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                ws->focus = TAILQ_FIRST(&ws->winlist);
        winfocus = cur_focus ? cur_focus : ws->focus;
 
+       win = TAILQ_FIRST(&ws->winlist);
        if (rot) {
+               w_inc = win->sh.width_inc;
+               w_base = win->sh.base_width;
                mwin = ws->l_state.horizontal_mwin;
                mscale = ws->l_state.horizontal_msize;
                SWAPXY(&r_g);
        } else {
+               w_inc = win->sh.height_inc;
+               w_base = win->sh.base_height;
                mwin = ws->l_state.vertical_mwin;
                mscale = ws->l_state.vertical_msize;
        }
        win_g = r_g;
 
+       h_slice = r_g.h / SWM_H_SLICE;
        if (mwin && winno > mwin) {
+               v_slice = r_g.w / SWM_V_SLICE;
+
                split = mwin;
                colno = split;
-               msize = (r_g.w / SWM_V_SLICE) * mscale;
+               msize = v_slice * mscale;
+
+               if (w_inc > 1 && w_inc < v_slice) {
+                       /* adjust for window's requested size increment */
+                       remain = (win_g.w - w_base) % w_inc;
+                       missing = w_inc - remain;
+
+                       if (missing <= extra || j == 0) {
+                               extra -= missing;
+                               win_g.w += missing;
+                       } else {
+                               win_g.w -= remain;
+                               extra += remain;
+                       }
+               }
+
                win_g.w = msize;
+               if (flip) 
+                       win_g.x += r_g.w - msize;
        } else {
                colno = winno;
                split = 0;
        }
        hrh = r_g.h / colno;
+       extra = r_g.h - (colno * hrh);
        win_g.h = hrh - 2;
 
        i = j = 0;
@@ -1182,19 +1265,42 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                if (split && i == split) {
                        colno = winno - split;
                        hrh = (r_g.h / colno);
-                       win_g.x += msize + 2;
+                       extra = r_g.h - (colno * hrh);
+                       if (flip)
+                               win_g.x = r_g.x;
+                       else
+                               win_g.x += msize + 2;
                        win_g.w = r_g.w - (msize + 2);
-                       win_g.h = hrh - 2;
                        j = 0;
                }
-               if (j == colno - 1)
-                       win_g.h = (hrh + (r_g.h - (colno * hrh)));
+               win_g.h = hrh - 2;
+               if (rot) {
+                       h_inc = win->sh.width_inc;
+                       h_base = win->sh.base_width;
+               } else {
+                       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) {
+                       /* adjust for window's requested size increment */
+                       remain = (win_g.h - h_base) % h_inc;
+                       missing = h_inc - remain;
+
+                       if (missing <= extra || j == 0) {
+                               extra -= missing;
+                               win_g.h += missing;
+                       } else {
+                               win_g.h -= remain;
+                               extra += remain;
+                       }
+               }
                 
                if (j == 0)
                        win_g.y = r_g.y;
                else
-                       win_g.y += hrh;
-
+                       win_g.y += last_h + 2;
 
                if (win->transient != 0 || win->floating != 0)
                        stack_floater(win, ws->r);
@@ -1220,6 +1326,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                }
 
                XMapRaised(display, win->id);
+               last_h = win_g.h;
                i++;
                j++;
        }
@@ -1418,10 +1525,10 @@ screenshot(struct swm_region *r, union arg *args)
 
        switch (args->id) {
        case SWM_ARG_ID_SS_ALL:
-               spawn_screenshot[1] = "-m";
+               spawn_screenshot[1] = "full";
                break;
        case SWM_ARG_ID_SS_WINDOW:
-               spawn_screenshot[1] = "-s"; /* XXX doesn't work with scrot */
+               spawn_screenshot[1] = "window";
                break;
        default:
                return;
@@ -1466,6 +1573,8 @@ struct key {
        { MODKEY,               XK_0,           switchws,       {.id = 9} },
        { MODKEY,               XK_Right,       cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} }, 
        { MODKEY,               XK_Left,        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, 
+       { MODKEY | ShiftMask,   XK_Right,       cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} }, 
+       { MODKEY | ShiftMask,   XK_Left,        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} }, 
        { MODKEY | ShiftMask,   XK_1,           send_to_ws,     {.id = 0} },
        { MODKEY | ShiftMask,   XK_2,           send_to_ws,     {.id = 1} },
        { MODKEY | ShiftMask,   XK_3,           send_to_ws,     {.id = 2} },
@@ -1654,6 +1763,9 @@ manage_window(Window id)
        Atom                    ws_idx_atom = 0, type;
        unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
        struct swm_region       *r;
+       long                    mask;
+       int                     ws_idx;
+       const char              *errstr;
 
        if ((win = find_window(id)) != NULL)
                        return (win);   /* already being managed */
@@ -1670,9 +1782,6 @@ manage_window(Window id)
        r = root_to_region(win->wa.root);
        /* If the window was managed before, put it in the same workspace */
        if (prop) {
-               int             ws_idx;
-               const char      *errstr;
-
                DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
                ws_idx = strtonum(prop, 0, 9, &errstr);
                if (errstr)
@@ -1687,9 +1796,6 @@ manage_window(Window id)
        win->s = r->s;  /* this never changes */
        TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
 
-       /* make new win focused */
-       focus_win(win);
-
        XGetTransientForHint(display, win->id, &trans);
        if (trans) {
                win->transient = trans;
@@ -1701,6 +1807,8 @@ manage_window(Window id)
        win->g.x = win->wa.x;
        win->g.y = win->wa.y;
 
+       XGetWMNormalHints(display, win->id, &win->sh, &mask);
+
        if (ws_idx_atom && prop == NULL &&
            snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
                DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
@@ -1732,6 +1840,10 @@ manage_window(Window id)
 
        set_win_state(win, NormalState);
 
+       /* make new win focused */
+       focus_win(win);
+
+
        return (win);
 }
 
@@ -1824,16 +1936,16 @@ destroynotify(XEvent *e)
                        ws->focus = TAILQ_PREV(win, ws_win_list, entry);
                if (ws->focus == NULL)
                        ws->focus = TAILQ_FIRST(&ws->winlist);
-               if (ws->focus == win)
+               if (ws->focus == NULL || ws->focus == win) {
                        ws->focus = NULL;
-               if (cur_focus == win)
+                       unfocus_all();
+               } else
                        focus_win(ws->focus);
-
                TAILQ_REMOVE(&ws->winlist, win, entry);
                set_win_state(win, WithdrawnState);
                free(win);
+               stack();
        }
-       stack();
 }
 
 void
@@ -2159,7 +2271,8 @@ main(int argc, char *argv[])
        fd_set                  rd;
 
        start_argv = argv;
-       fprintf(stderr, "Welcome to scrotwm V%s\n", SWM_VERSION);
+       fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
+           SWM_VERSION, cvstag);
        if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
                warnx("no locale support");