JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Add comment and link to man page. Turns out a lot of people don't know
[spectrwm.git] / scrotwm.c
index 92ea40d..10806c9 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -54,7 +54,7 @@
 static const char      *cvstag =
     "$scrotwm$";
 
-#define        SWM_VERSION     "0.9.30"
+#define        SWM_VERSION     "0.9.32"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -292,6 +292,15 @@ struct ws_win {
 };
 TAILQ_HEAD(ws_win_list, ws_win);
 
+/* pid goo */
+struct pid_e {
+       TAILQ_ENTRY(pid_e)      entry;
+       long                    pid;
+       int                     ws;
+};
+TAILQ_HEAD(pid_list, pid_e);
+struct pid_list                        pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
+
 /* layout handlers */
 void   stack(void);
 void   vertical_config(struct workspace *, int);
@@ -323,8 +332,10 @@ struct layout {
        { NULL,                 NULL,                   0,      NULL  },
 };
 
-/* position of max_stack mode in the layouts array */
-#define SWM_MAX_STACK          2
+/* position of max_stack mode in the layouts array, index into layouts! */
+#define SWM_V_STACK            (0)
+#define SWM_H_STACK            (1)
+#define SWM_MAX_STACK          (2)
 
 #define SWM_H_SLICE            (32)
 #define SWM_V_SLICE            (32)
@@ -332,6 +343,7 @@ struct layout {
 /* define work spaces */
 struct workspace {
        int                     idx;            /* workspace index */
+       int                     always_raise;   /* raise windows on focus */
        struct layout           *cur_layout;    /* current layout handlers */
        struct ws_win           *focus;         /* may be NULL */
        struct ws_win           *focus_prev;    /* may be NULL */
@@ -1047,6 +1059,24 @@ sighdlr(int sig)
        errno = saved_errno;
 }
 
+struct pid_e *
+find_pid(long pid)
+{
+       struct pid_e            *p = NULL;
+
+       DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid);
+
+       if (pid == 0)
+               return (NULL);
+
+       TAILQ_FOREACH(p, &pidlist, entry) {
+               if (p->pid == pid)
+                       return (p);
+       }
+
+       return (NULL);
+}
+
 unsigned long
 name_to_color(char *colorname)
 {
@@ -1687,7 +1717,7 @@ find_window(Window id)
 }
 
 void
-spawn(struct swm_region *r, union arg *args, int close_fd)
+spawn(int ws_idx, union arg *args, int close_fd)
 {
        int                     fd;
        char                    *ret = NULL;
@@ -1699,7 +1729,7 @@ spawn(struct swm_region *r, union arg *args, int close_fd)
 
        setenv("LD_PRELOAD", SWM_LIB, 1);
 
-       if (asprintf(&ret, "%d", r->ws->idx) == -1) {
+       if (asprintf(&ret, "%d", ws_idx) == -1) {
                perror("_SWM_WS");
                _exit(1);
        }
@@ -1747,10 +1777,11 @@ spawnterm(struct swm_region *r, union arg *args)
 {
        DNPRINTF(SWM_D_MISC, "spawnterm\n");
 
-       if (term_width)
-               setenv("_SWM_XTERM_FONTADJ", "", 1);
-       if (fork() == 0)
-               spawn(r, args, 1);
+       if (fork() == 0) {
+               if (term_width)
+                       setenv("_SWM_XTERM_FONTADJ", "", 1);
+               spawn(r->ws->idx, args, 1);
+       }
 }
 
 void
@@ -1929,11 +1960,11 @@ focus_win(struct ws_win *win)
                if (win->java == 0)
                        XSetInputFocus(display, win->id,
                            RevertToParent, CurrentTime);
-               XMapRaised(display, win->id);
                grabbuttons(win, 1);
                XSetWindowBorder(display, win->id,
                    win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
-               if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS)
+               if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
+                   win->ws->always_raise)
                        XMapRaised(display, win->id);
 
                XChangeProperty(display, win->s->root,
@@ -2972,6 +3003,19 @@ send_to_ws(struct swm_region *r, union arg *args)
 }
 
 void
+raise_toggle(struct swm_region *r, union arg *args)
+{
+       if (r && r->ws == NULL)
+               return;
+
+       r->ws->always_raise = !r->ws->always_raise;
+
+       /* bring floaters back to top */
+       if (r->ws->always_raise == 0)
+               stack();
+}
+
+void
 iconify(struct swm_region *r, union arg *args)
 {
        union arg a;
@@ -3456,6 +3500,7 @@ enum keyfuncid {
        kf_spawn_custom,
        kf_iconify,
        kf_uniconify,
+       kf_raise_toggle,
        kf_dumpwins, /* MUST BE LAST */
        kf_invalid
 };
@@ -3532,6 +3577,7 @@ struct keyfunc {
        { "spawn_custom",       dummykeyfunc,   {0} },
        { "iconify",            iconify,        {0} },
        { "uniconify",          uniconify,      {0} },
+       { "raise_toggle",       raise_toggle,   {0} },
        { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
        { "invalid key func",   NULL,           {0} },
 };
@@ -3678,7 +3724,7 @@ spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
                return;
        a.argv = real_args;
        if (fork() == 0)
-               spawn(r, &a, 1);
+               spawn(r->ws->idx, &a, 1);
 
        for (i = 0; i < spawn_argc; i++)
                free(real_args[i]);
@@ -3714,7 +3760,7 @@ spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
                        errx(1, "dup2");
                close(select_list_pipe[1]);
                close(select_resp_pipe[0]);
-               spawn(r, &a, 0);
+               spawn(r->ws->idx, &a, 0);
                break;
        default: /* parent */
                close(select_list_pipe[0]);
@@ -3835,8 +3881,9 @@ setconfspawn(char *selector, char *value, int flags)
                        cp += (long)strspn(cp, " \t");
                if (strlen(word) > 0) {
                        prog->argc++;
-                       prog->argv = realloc(prog->argv,
-                           prog->argc * sizeof(char *));
+                       if ((prog->argv = realloc(prog->argv,
+                           prog->argc * sizeof(char *))) == NULL)
+                               err(1, "setconfspawn: realloc");
                        if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL)
                                err(1, "setconfspawn: strdup");
                }
@@ -3983,7 +4030,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
                keys_size = 4;
                DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
                keys = malloc((size_t)keys_size * sizeof(struct key));
-               if (!keys) {
+               if (keys == NULL) {
                        fprintf(stderr, "malloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -3992,7 +4039,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
                keys_size *= 2;
                DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
                keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
-               if (!keys) {
+               if (keys == NULL) {
                        fprintf(stderr, "realloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -4008,7 +4055,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
                keys[j].spawn_name = strdupsafe(spawn_name);
        } else {
                fprintf(stderr, "keys array problem?\n");
-               if (!keys) {
+               if (keys == NULL) {
                        fprintf(stderr, "keys array problem\n");
                        quit(NULL, NULL);
                }
@@ -4122,6 +4169,7 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,        "initscr");
        setkeybinding(MODKEY,           XK_w,           kf_iconify,     NULL);
        setkeybinding(MODKEY|ShiftMask, XK_w,           kf_uniconify,   NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_r,           kf_raise_toggle,NULL);
 #ifdef SWM_DEBUG
        setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
 #endif
@@ -4286,7 +4334,7 @@ setquirk(const char *class, const char *name, const int quirk)
                quirks_size = 4;
                DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
                quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
-               if (!quirks) {
+               if (quirks == NULL) {
                        fprintf(stderr, "setquirk: malloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -4296,7 +4344,7 @@ setquirk(const char *class, const char *name, const int quirk)
                DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
                quirks = realloc(quirks,
                    (size_t)quirks_size * sizeof(struct quirk));
-               if (!quirks) {
+               if (quirks == NULL) {
                        fprintf(stderr, "setquirk: realloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -4310,7 +4358,7 @@ setquirk(const char *class, const char *name, const int quirk)
                quirks[j].quirk = quirk;
        } else {
                fprintf(stderr, "quirks array problem?\n");
-               if (!quirks) {
+               if (quirks == NULL) {
                        fprintf(stderr, "quirks array problem!\n");
                        quit(NULL, NULL);
                }
@@ -4490,17 +4538,147 @@ setconfregion(char *selector, char *value, int flags)
        return (0);
 }
 
+int
+setautorun(char *selector, char *value, int flags)
+{
+       int                     ws_id;
+       char                    s[1024];
+       char                    *ap, *sp = s;
+       union arg               a;
+       int                     argc = 0;
+       long                    pid;
+       struct pid_e            *p;
+
+       if (getenv("SWM_STARTED"))
+               return (0);
+
+       bzero(s, sizeof s);
+       if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
+               errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'\n");
+       ws_id--;
+       if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+               errx(1, "autorun: invalid workspace %d\n", ws_id + 1);
+
+       /*
+        * This is a little intricate
+        *
+        * If the pid already exists we simply reuse it because it means it was
+        * used before AND not claimed by manage_window.  We get away with
+        * altering it in the parent after INSERT because this can not be a race
+        */
+       a.argv = NULL;
+       while ((ap = strsep(&sp, " \t")) != NULL) {
+               if (*ap == '\0')
+                       continue;
+               DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
+               argc++;
+               if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
+                       err(1, "setautorun: realloc");
+               a.argv[argc - 1] = ap;
+       }
+
+       if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
+               err(1, "setautorun: realloc");
+       a.argv[argc] = NULL;
+
+       if ((pid = fork()) == 0) {
+               spawn(ws_id, &a, 1);
+               /* NOTREACHED */
+               _exit(1);
+       }
+       free(a.argv);
+
+       /* parent */
+       p = find_pid(pid);
+       if (p == NULL) {
+               p = calloc(1, sizeof *p);
+               if (p == NULL)
+                       return (1);
+               TAILQ_INSERT_TAIL(&pidlist, p, entry);
+       }
+
+       p->pid = pid;
+       p->ws = ws_id;
+
+       return (0);
+}
+
+int
+setlayout(char *selector, char *value, int flags)
+{
+       int                     ws_id, st, i, x, mg, ma, si, raise;
+       char                    s[1024];
+       struct workspace        *ws;
+
+       if (getenv("SWM_STARTED"))
+               return (0);
+
+       bzero(s, sizeof s);
+       if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
+           &ws_id, &mg, &ma, &si, &raise, s) != 6)
+               errx(1, "invalid layout entry, should be 'ws[<idx>]:"
+                   "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
+                   "<type>'\n");
+       ws_id--;
+       if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+               errx(1, "layout: invalid workspace %d\n", ws_id + 1);
+
+       if (!strcasecmp(s, "vertical"))
+               st = SWM_V_STACK;
+       else if (!strcasecmp(s, "horizontal"))
+               st = SWM_H_STACK;
+       else if (!strcasecmp(s, "fullscreen"))
+               st = SWM_MAX_STACK;
+       else
+               errx(1, "invalid layout entry, should be 'ws[<idx>]:"
+                   "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
+                   "<type>'\n");
+
+       for (i = 0; i < ScreenCount(display); i++) {
+               ws = (struct workspace *)&screens[i].ws;
+               ws[ws_id].cur_layout = &layouts[st];
+
+               ws[ws_id].always_raise = raise;
+               if (st == SWM_MAX_STACK)
+                       continue;
+
+               /* master grow */
+               for (x = 0; x < abs(mg); x++) {
+                       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();
+               }
+       }
+
+       return (0);
+}
+
 /* config options */
 struct config_option {
        char                    *optname;
-       int (*func)(char*, char*, int);
-       int funcflags;
+       int                     (*func)(char*, char*, int);
+       int                     funcflags;
 };
 struct config_option configopt[] = {
        { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
        { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
        { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
-       { "bar_border_width",                   setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
+       { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
        { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
        { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
        { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
@@ -4526,9 +4704,11 @@ struct config_option configopt[] = {
        { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
        { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
        { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
-       { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
-       { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
-       { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
+       { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
+       { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
+       { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
+       { "autorun",                    setautorun,     0 },
+       { "layout",                     setlayout,      0 },
 };
 
 
@@ -4677,6 +4857,52 @@ set_child_transient(struct ws_win *win, Window *trans)
                XFree(wmh);
 }
 
+long
+window_get_pid(Window win)
+{
+       Atom                    actual_type_return;
+       int                     actual_format_return = 0;
+       unsigned long           nitems_return = 0;
+       unsigned long           bytes_after_return = 0;
+       long                    *pid = NULL;
+       long                    ret = 0;
+       const char              *errstr;
+       unsigned char           *prop = NULL;
+
+       if (XGetWindowProperty(display, win,
+           XInternAtom(display, "_NET_WM_PID", False), 0, 1, False,
+           XA_CARDINAL, &actual_type_return, &actual_format_return,
+           &nitems_return, &bytes_after_return,
+           (unsigned char**)(void*)&pid) != Success)
+               goto tryharder;
+       if (actual_type_return != XA_CARDINAL)
+               goto tryharder;
+       if (pid == NULL)
+               goto tryharder;
+
+       ret = *pid;
+       XFree(pid);
+
+       return (ret);
+
+tryharder:
+       if (XGetWindowProperty(display, win,
+           XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False,
+           XA_STRING, &actual_type_return, &actual_format_return,
+           &nitems_return, &bytes_after_return, &prop) != Success)
+               return (0);
+       if (actual_type_return != XA_STRING)
+               return (0);
+       if (prop == NULL)
+               return (0);
+
+       ret = strtonum(prop, 0, UINT_MAX, &errstr);
+       /* ignore error because strtonum returns 0 anyway */
+       XFree(prop);
+
+       return (ret);
+}
+
 struct ws_win *
 manage_window(Window id)
 {
@@ -4692,6 +4918,7 @@ manage_window(Window id)
        long                    mask;
        const char              *errstr;
        XWindowChanges          wc;
+       struct pid_e            *p;
 
        if ((win = find_window(id)) != NULL)
                return (win);   /* already being managed */
@@ -4716,11 +4943,15 @@ manage_window(Window id)
 
        win->id = id;
 
+       /* see if we need to override the workspace */
+       p = find_pid(window_get_pid(id));
+
        /* Get all the window data in one shot */
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
-       if (ws_idx_atom)
+       if (ws_idx_atom) {
                XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
                    False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
+       }
        XGetWindowAttributes(display, id, &win->wa);
        XGetWMNormalHints(display, id, &win->sh, &mask);
        win->hints = XGetWMHints(display, id);
@@ -4752,7 +4983,12 @@ manage_window(Window id)
         * transient, * put it in the same workspace
         */
        r = root_to_region(win->wa.root);
-       if (prop && win->transient == 0) {
+       if (p) {
+               ws = &r->s->ws[p->ws];
+               TAILQ_REMOVE(&pidlist, p, entry);
+               free(p);
+               p = NULL;
+       } else if (prop && win->transient == 0) {
                DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
                ws_idx = strtonum(prop, 0, 9, &errstr);
                if (errstr) {
@@ -5701,14 +5937,11 @@ setup_screens(void)
        int                     i, j, k;
        int                     errorbase, major, minor;
        struct workspace        *ws;
-       int                     ws_idx_atom;
 
        if ((screens = calloc(ScreenCount(display),
             sizeof(struct swm_screen))) == NULL)
                errx(1, "calloc: screens");
 
-       ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
-
        /* initial Xrandr setup */
        xrandr_support = XRRQueryExtension(display,
            &xrandr_eventbase, &errorbase);
@@ -5888,6 +6121,9 @@ main(int argc, char *argv[])
        /* grab existing windows (before we build the bars) */
        grab_windows();
 
+       if (getenv("SWM_STARTED") == NULL)
+               setenv("SWM_STARTED", "YES", 1);
+
        /* setup all bars */
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry) {