JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Add class code so that we can quirk certain apps.
[spectrwm.git] / scrotwm.c
index 62f4c25..7c07648 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_STACK             0x0020
 #define        SWM_D_MOUSE             0x0040
 #define        SWM_D_PROP              0x0080
-
+#define        SWM_D_CLASS             0x0100
 
 u_int32_t              swm_debug = 0
                            | SWM_D_MISC
@@ -111,7 +113,9 @@ u_int32_t           swm_debug = 0
                            | SWM_D_FOCUS
                            | SWM_D_MOVE
                            | SWM_D_STACK
+                           | SWM_D_MOUSE
                            | SWM_D_PROP
+                           | SWM_D_CLASS
                            ;
 #else
 #define DPRINTF(x...)
@@ -122,18 +126,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 +177,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)
@@ -302,11 +304,24 @@ 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;
 };
 
+struct quirk {
+       char                    *class;
+       char                    *name;
+       unsigned long           quirk;
+#define SWM_Q_FLOAT            (1<<0)
+} quirks[] = {
+       { "MPlayer",            "xv",           SWM_Q_FLOAT },
+       { "OpenOffice.org 2.4", "VCLSalFrame",  SWM_Q_FLOAT },
+       { NULL,         NULL,           0},
+};
+
 unsigned long
 name_to_color(char *colorname)
 {
@@ -457,10 +472,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;
@@ -720,6 +738,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]);
@@ -731,6 +761,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");
@@ -785,6 +816,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
@@ -792,15 +826,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");
@@ -897,7 +940,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 {
@@ -926,7 +968,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:
@@ -955,6 +996,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;
@@ -1039,7 +1107,7 @@ focus(struct swm_region *r, union arg *args)
                return;
        }
 
-       if (winfocus == winlostfocus)
+       if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
        XMapRaised(display, winfocus->id);
@@ -1058,7 +1126,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();
 }
 
@@ -1145,6 +1212,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        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",
@@ -1174,7 +1242,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
 
        h_slice = r_g.h / SWM_H_SLICE;
        if (mwin && winno > mwin) {
-               int v_slice = r_g.w / SWM_V_SLICE;
+               v_slice = r_g.w / SWM_V_SLICE;
 
                split = mwin;
                colno = split;
@@ -1182,8 +1250,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
 
                if (w_inc > 1 && w_inc < v_slice) {
                        /* adjust for window's requested size increment */
-                       int remain = (win_g.w - w_base) % w_inc;
-                       int missing = w_inc - remain;
+                       remain = (win_g.w - w_base) % w_inc;
+                       missing = w_inc - remain;
 
                        if (missing <= extra || j == 0) {
                                extra -= missing;
@@ -1230,8 +1298,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        win_g.h = hrh + extra;
                } else if (h_inc > 1 && h_inc < h_slice) {
                        /* adjust for window's requested size increment */
-                       int remain = (win_g.h - h_base) % h_inc;
-                       int missing = h_inc - remain;
+                       remain = (win_g.h - h_base) % h_inc;
+                       missing = h_inc - remain;
 
                        if (missing <= extra || j == 0) {
                                extra -= missing;
@@ -1470,10 +1538,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;
@@ -1518,6 +1586,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} },
@@ -1701,12 +1771,13 @@ manage_window(Window id)
        struct workspace        *ws;
        struct ws_win           *win;
        XClassHint              ch;
-       int                     format;
+       int                     format, i, ws_idx;
        unsigned long           nitems, bytes;
        Atom                    ws_idx_atom = 0, type;
        unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
        struct swm_region       *r;
        long                    mask;
+       const char              *errstr;
 
        if ((win = find_window(id)) != NULL)
                        return (win);   /* already being managed */
@@ -1723,9 +1794,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)
@@ -1740,9 +1808,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;
@@ -1754,7 +1819,7 @@ manage_window(Window id)
        win->g.x = win->wa.x;
        win->g.y = win->wa.y;
 
-       XGetWMNormalHints(display, win->id, &win->sh, &mask);
+       XGetWMNormalHints(display, win->id, &win->sh, &mask); /* XXX function? */
 
        if (ws_idx_atom && prop == NULL &&
            snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
@@ -1769,17 +1834,27 @@ manage_window(Window id)
        fprintf(stderr, "manage window: %d x %d y %d w %d h %d\n", win->id, win->g.x, win->g.y, win->g.w, win->g.h);
        */
 
-       /* XXX make this a table */
        bzero(&ch, sizeof ch);
        if (XGetClassHint(display, win->id, &ch)) {
-               /*fprintf(stderr, "class: %s name: %s\n", ch.res_class, ch.res_name); */
-               if (!strcmp(ch.res_class, "MPlayer") && !strcmp(ch.res_name, "xv")) {
-                       win->floating = 1;
-               }
+               DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
+                   ch.res_class, ch.res_name);
+               for (i = 0; quirks[i].class != NULL && quirks[i].name != NULL &&
+                   quirks[i].quirk != 0; i++){
+                       if (!strcmp(ch.res_class, quirks[i].class) &&
+                           !strcmp(ch.res_name, quirks[i].name)) {
+                               DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n",
+                                   ch.res_class, ch.res_name);
+                               if (quirks[i].quirk & SWM_Q_FLOAT)
+                                       win->floating = 1;
+                       }
+#if 0
+               /* XXX logic says to do this but get a double free */
                if (ch.res_class)
                        XFree(ch.res_class);
                if (ch.res_name)
                        XFree(ch.res_name);
+#endif
+               }
        }
 
        XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
@@ -1787,6 +1862,9 @@ manage_window(Window id)
 
        set_win_state(win, NormalState);
 
+       /* make new win focused */
+       focus_win(win);
+
        return (win);
 }
 
@@ -1835,15 +1913,16 @@ configurerequest(XEvent *e)
                                win->g.h = ev->height;
                        if (win->ws->r != NULL) {
                                /* this seems to be full screen */
-                               if (win->g.w > WIDTH(win->ws->r)) {
-                                       /* kill border */
-                                       win->g.x -= 1;
-                                       win->g.w += 1;
+                               if (win->g.w >= WIDTH(win->ws->r)) {
+                                       win->g.x = -1;
+                                       win->g.w = WIDTH(win->ws->r);
+                                       ev->value_mask |= CWX | CWWidth;
                                }
-                               if (win->g.h > HEIGHT(win->ws->r)) {
+                               if (win->g.h >= HEIGHT(win->ws->r)) {
                                        /* kill border */
-                                       win->g.y -= 1;
-                                       win->g.h += 1;
+                                       win->g.y = -1;
+                                       win->g.h = HEIGHT(win->ws->r);
+                                       ev->value_mask |= CWY | CWHeight;
                                }
                        }
                        if ((ev->value_mask & (CWX|CWY)) &&
@@ -1879,16 +1958,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
@@ -1947,8 +2026,36 @@ maprequest(XEvent *e)
 void
 propertynotify(XEvent *e)
 {
+       struct ws_win           *win;
+       XPropertyEvent          *ev = &e->xproperty;
+
        DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
-           e->xproperty.window);
+           ev->window);
+
+       if (ev->state == PropertyDelete)
+               return; /* ignore */
+       win = find_window(ev->window);
+       if (win == NULL)
+               return;
+
+       switch (ev->atom) {
+       case XA_WM_NORMAL_HINTS:
+#if 0
+               long            mask;
+               XGetWMNormalHints(display, win->id, &win->sh, &mask);
+               fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
+               if (win->sh.flags & PMinSize) {
+                       win->g.w = win->sh.min_width;
+                       win->g.h = win->sh.min_height;
+                       fprintf(stderr, "min %d %d\n", win->g.w, win->g.h);
+               }
+               XMoveResizeWindow(display, win->id,
+                   win->g.x, win->g.y, win->g.w, win->g.h);
+#endif
+               break;
+       default:
+               break;
+       }
 }
 
 void
@@ -2214,7 +2321,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");