]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Resolve memory leaks on exit.
[spectrwm] / spectrwm.c
index b01ed60e60f6908db8a8b08fd1b62715dfe096cd..1542fe1e512a5d38b06cbe2ad776681172ca147d 100644 (file)
@@ -1,14 +1,15 @@
 /*
- * Copyright (c) 2009-2012 Marco Peereboom <marco@peereboom.us>
+ * Copyright (c) 2009-2015 Marco Peereboom <marco@peereboom.us>
  * Copyright (c) 2009-2011 Ryan McBride <mcbride@countersiege.com>
  * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
  * 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-2014 Reginald Kennedy <rk@rejii.com>
+ * Copyright (c) 2011-2015 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>
+ * Copyright (c) 2012-2015 David Hill <dhill@mindcry.org>
+ * Copyright (c) 2014-2015 Yuri D'Elia <yuri.delia@eurac.edu>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -332,7 +333,9 @@ bool                        cycle_visible = false;
 int                    term_width = 0;
 int                    font_adjusted = 0;
 unsigned int           mod_key = MODKEY;
+bool                   warp_focus = false;
 bool                   warp_pointer = false;
+bool                   workspace_clamp = false;
 
 /* dmenu search */
 struct swm_region      *search_r;
@@ -402,6 +405,7 @@ char                 *bar_format = NULL;
 bool            stack_enabled = true;
 bool            clock_enabled = true;
 bool            iconic_enabled = false;
+bool            maximize_hide_bar = false;
 bool            urgent_enabled = false;
 bool            urgent_collapse = false;
 char           *clock_format = NULL;
@@ -625,6 +629,7 @@ union arg {
 #define SWM_ARG_ID_FLIPLAYOUT  (24)
 #define SWM_ARG_ID_STACKRESET  (30)
 #define SWM_ARG_ID_STACKINIT   (31)
+#define SWM_ARG_ID_STACKBALANCE        (32)
 #define SWM_ARG_ID_CYCLEWS_UP  (40)
 #define SWM_ARG_ID_CYCLEWS_DOWN        (41)
 #define SWM_ARG_ID_CYCLERG_UP  (42)
@@ -653,6 +658,8 @@ union arg {
 #define SWM_ARG_ID_LOWER       (106)
 #define SWM_ARG_ID_BAR_TOGGLE  (110)
 #define SWM_ARG_ID_BAR_TOGGLE_WS       (111)
+#define SWM_ARG_ID_CYCLERG_MOVE_UP     (112)
+#define SWM_ARG_ID_CYCLERG_MOVE_DOWN   (113)
        char                    **argv;
 };
 
@@ -678,6 +685,7 @@ struct quirk {
 #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. */
+#define SWM_Q_NOFOCUSCYCLE     (1<<11) /* Remove from normal focus cycle. */
 };
 TAILQ_HEAD(quirk_list, quirk);
 struct quirk_list              quirks = TAILQ_HEAD_INITIALIZER(quirks);
@@ -881,6 +889,8 @@ enum keyfuncid {
        KF_RG_7,
        KF_RG_8,
        KF_RG_9,
+       KF_RG_MOVE_NEXT,
+       KF_RG_MOVE_PREV,
        KF_RG_NEXT,
        KF_RG_PREV,
        KF_SCREEN_NEXT,
@@ -888,6 +898,7 @@ enum keyfuncid {
        KF_SEARCH_WIN,
        KF_SEARCH_WORKSPACE,
        KF_SPAWN_CUSTOM,
+       KF_STACK_BALANCE,
        KF_STACK_INC,
        KF_STACK_DEC,
        KF_STACK_RESET,
@@ -1088,6 +1099,7 @@ void       print_win_geom(xcb_window_t);
 #endif
 void    propertynotify(xcb_property_notify_event_t *);
 void    quirk_free(struct quirk *);
+void    clear_quirks(void);
 void    quirk_insert(const char *, const char *, const char *, uint32_t, int);
 void    quirk_remove(struct quirk *);
 void    quirk_replace(struct quirk *, const char *, const char *, const char *,
@@ -1147,6 +1159,7 @@ int        spawn_expand(struct swm_region *, union arg *, const char *, char ***);
 void    spawn_insert(const char *, const char *, int);
 struct spawn_prog      *spawn_find(const char *);
 void    spawn_remove(struct spawn_prog *);
+void    clear_spawns(void);
 void    spawn_replace(struct spawn_prog *, const char *, const char *, int);
 void    spawn_select(struct swm_region *, union arg *, const char *, int *);
 void    stack_config(struct swm_region *, union arg *);
@@ -2213,6 +2226,8 @@ bar_urgent(char *s, size_t sz)
                        strlcat(s, "- ", sz);
                }
        }
+       if(urgent_collapse && s[0])
+               s[strlen(s) - 1] = 0;
 }
 
 void
@@ -3872,6 +3887,20 @@ switchws(struct swm_region *r, union arg *args)
        if (new_ws == old_ws)
                return;
 
+       other_r = new_ws->r;
+       if (other_r && workspace_clamp) {
+               DNPRINTF(SWM_D_WS, "switchws: ws clamped.\n");
+
+               if (warp_focus) {
+                       DNPRINTF(SWM_D_WS, "switchws: warping focus to region "
+                           "with ws %d.\n", wsid);
+                       focus_region(other_r);
+                       center_pointer(other_r);
+                       focus_flush();
+               }
+               return;
+       }
+
        if ((win = old_ws->focus) != NULL) {
                update_window_color(win);
 
@@ -3880,17 +3909,17 @@ switchws(struct swm_region *r, union arg *args)
                    &none);
        }
 
-       other_r = new_ws->r;
-       if (other_r == NULL) {
-               /* the other workspace is hidden, hide this one */
-               old_ws->r = NULL;
-               unmap_old = true;
-       } else {
+       if (other_r) {
                /* the other ws is visible in another region, exchange them */
                other_r->ws_prior = new_ws;
                other_r->ws = old_ws;
                old_ws->r = other_r;
+       } else {
+               /* the other workspace is hidden, hide this one */
+               old_ws->r = NULL;
+               unmap_old = true;
        }
+
        this_r->ws_prior = old_ws;
        this_r->ws = new_ws;
        new_ws->r = this_r;
@@ -4035,6 +4064,7 @@ focusrg(struct swm_region *r, union arg *args)
 void
 cyclerg(struct swm_region *r, union arg *args)
 {
+       union arg               a;
        struct swm_region       *rr = NULL;
        int                     i, num_screens;
 
@@ -4048,11 +4078,13 @@ cyclerg(struct swm_region *r, union arg *args)
 
        switch (args->id) {
        case SWM_ARG_ID_CYCLERG_UP:
+       case SWM_ARG_ID_CYCLERG_MOVE_UP:
                rr = TAILQ_NEXT(r, entry);
                if (rr == NULL)
                        rr = TAILQ_FIRST(&screens[i].rl);
                break;
        case SWM_ARG_ID_CYCLERG_DOWN:
+       case SWM_ARG_ID_CYCLERG_MOVE_DOWN:
                rr = TAILQ_PREV(r, swm_region_list, entry);
                if (rr == NULL)
                        rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
@@ -4063,9 +4095,22 @@ cyclerg(struct swm_region *r, union arg *args)
        if (rr == NULL)
                return;
 
-       focus_region(rr);
-       center_pointer(rr);
-       focus_flush();
+       switch (args->id) {
+       case SWM_ARG_ID_CYCLERG_UP:
+       case SWM_ARG_ID_CYCLERG_DOWN:
+               focus_region(rr);
+               center_pointer(rr);
+               focus_flush();
+               break;
+       case SWM_ARG_ID_CYCLERG_MOVE_UP:
+       case SWM_ARG_ID_CYCLERG_MOVE_DOWN:
+               a.id = rr->ws->idx;
+               switchws(r, &a);
+               break;
+       default:
+               return;
+       };
+
        DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n");
 }
 
@@ -4388,7 +4433,8 @@ focus(struct swm_region *r, union arg *args)
                } while (winfocus && (ICONIC(winfocus) ||
                    winfocus->id == cur_focus->transient ||
                    (cur_focus->transient != XCB_WINDOW_NONE &&
-                   winfocus->transient == cur_focus->transient)));
+                   winfocus->transient == cur_focus->transient) ||
+                   (winfocus->quirks & SWM_Q_NOFOCUSCYCLE)));
                break;
        case SWM_ARG_ID_FOCUSNEXT:
                if (cur_focus == NULL)
@@ -4404,7 +4450,8 @@ focus(struct swm_region *r, union arg *args)
                } while (winfocus && (ICONIC(winfocus) ||
                    winfocus->id == cur_focus->transient ||
                    (cur_focus->transient != XCB_WINDOW_NONE &&
-                   winfocus->transient == cur_focus->transient)));
+                   winfocus->transient == cur_focus->transient) ||
+                   (winfocus->quirks & SWM_Q_NOFOCUSCYCLE)));
                break;
        case SWM_ARG_ID_FOCUSMAIN:
                if (cur_focus == NULL)
@@ -4644,7 +4691,7 @@ update_floater(struct ws_win *win)
 
                win->g = r->g;
 
-               if (bar_enabled && ws->bar_enabled) {
+               if (bar_enabled && ws->bar_enabled && !maximize_hide_bar) {
                        if (!bar_at_bottom)
                                Y(win) += bar_height;
                        HEIGHT(win) -= bar_height;
@@ -4954,6 +5001,9 @@ vertical_config(struct workspace *ws, int id)
                if (ws->l_state.vertical_mwin > 0)
                        ws->l_state.vertical_mwin--;
                break;
+       case SWM_ARG_ID_STACKBALANCE:
+               ws->l_state.vertical_msize = SWM_V_SLICE / (ws->l_state.vertical_stacks + 1);
+               break;
        case SWM_ARG_ID_STACKINC:
                ws->l_state.vertical_stacks++;
                break;
@@ -5004,6 +5054,9 @@ horizontal_config(struct workspace *ws, int id)
                if (ws->l_state.horizontal_mwin > 0)
                        ws->l_state.horizontal_mwin--;
                break;
+       case SWM_ARG_ID_STACKBALANCE:
+               ws->l_state.horizontal_msize = SWM_H_SLICE / (ws->l_state.horizontal_stacks + 1);
+               break;
        case SWM_ARG_ID_STACKINC:
                ws->l_state.horizontal_stacks++;
                break;
@@ -5807,10 +5860,13 @@ ewmh_update_current_desktop(void)
        int                     num_screens, i;
 
        num_screens = get_screen_count();
-       for (i = 0; i < num_screens; ++i)
-               xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
-                   screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom,
-                   XCB_ATOM_CARDINAL, 32, 1, &screens[i].r_focus->ws->idx);
+       for (i = 0; i < num_screens; ++i) {
+               if (screens[i].r_focus)
+                       xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
+                           screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom,
+                           XCB_ATOM_CARDINAL, 32, 1,
+                           &screens[i].r_focus->ws->idx);
+       }
 }
 
 void
@@ -6693,6 +6749,8 @@ struct keyfunc {
        { "rg_7",               focusrg,        {.id = 6} },
        { "rg_8",               focusrg,        {.id = 7} },
        { "rg_9",               focusrg,        {.id = 8} },
+       { "rg_move_next",       cyclerg,        {.id = SWM_ARG_ID_CYCLERG_MOVE_UP} },
+       { "rg_move_prev",       cyclerg,        {.id = SWM_ARG_ID_CYCLERG_MOVE_DOWN} },
        { "rg_next",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
        { "rg_prev",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
        { "screen_next",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
@@ -6700,6 +6758,7 @@ struct keyfunc {
        { "search_win",         search_win,     {0} },
        { "search_workspace",   search_workspace,       {0} },
        { "spawn_custom",       NULL,           {0} },
+       { "stack_balance",      stack_config,   {.id = SWM_ARG_ID_STACKBALANCE} },
        { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
        { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
        { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
@@ -7064,6 +7123,16 @@ spawn_remove(struct spawn_prog *sp)
        DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n");
 }
 
+void
+clear_spawns(void)
+{
+       struct spawn_prog       *sp;
+
+       while ((sp = TAILQ_FIRST(&spawns)) != NULL) {
+               spawn_remove(sp);
+       }
+}
+
 struct spawn_prog*
 spawn_find(const char *name)
 {
@@ -7677,6 +7746,7 @@ const char *quirkname[] = {
        "OBEYAPPFOCUSREQ",
        "IGNOREPID",
        "IGNORESPAWNWS",
+       "NOFOCUSCYCLE",
 };
 
 /* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */
@@ -7817,6 +7887,16 @@ quirk_free(struct quirk *qp)
        free(qp);
 }
 
+void
+clear_quirks(void)
+{
+       struct quirk            *qp;
+
+       while ((qp = TAILQ_FIRST(&quirks)) != NULL) {
+               quirk_remove(qp);
+       }
+}
+
 void
 quirk_replace(struct quirk *qp, const char *class, const char *instance,
     const char *name, uint32_t quirk, int ws)
@@ -7992,6 +8072,7 @@ enum {
        SWM_S_FOCUS_MODE,
        SWM_S_ICONIC_ENABLED,
        SWM_S_JAVA_WORKAROUND,
+       SWM_S_MAXIMIZE_HIDE_BAR,
        SWM_S_REGION_PADDING,
        SWM_S_SPAWN_ORDER,
        SWM_S_SPAWN_TERM,
@@ -8003,10 +8084,12 @@ enum {
        SWM_S_URGENT_COLLAPSE,
        SWM_S_URGENT_ENABLED,
        SWM_S_VERBOSE_LAYOUT,
+       SWM_S_WARP_FOCUS,
        SWM_S_WARP_POINTER,
        SWM_S_WINDOW_CLASS_ENABLED,
        SWM_S_WINDOW_INSTANCE_ENABLED,
        SWM_S_WINDOW_NAME_ENABLED,
+       SWM_S_WORKSPACE_CLAMP,
        SWM_S_WORKSPACE_LIMIT,
        SWM_S_WORKSPACE_NAME,
 };
@@ -8165,6 +8248,9 @@ setconfvalue(const char *selector, const char *value, int flags)
        case SWM_S_JAVA_WORKAROUND:
                java_workaround = (atoi(value) != 0);
                break;
+       case SWM_S_MAXIMIZE_HIDE_BAR:
+               maximize_hide_bar = atoi(value);
+               break;
        case SWM_S_REGION_PADDING:
                region_padding = atoi(value);
                if (region_padding < 0)
@@ -8218,6 +8304,9 @@ setconfvalue(const char *selector, const char *value, int flags)
                                layouts[i].l_string = plain_stacker;
                }
                break;
+       case SWM_S_WARP_FOCUS:
+               warp_focus = (atoi(value) != 0);
+               break;
        case SWM_S_WARP_POINTER:
                warp_pointer = (atoi(value) != 0);
                break;
@@ -8230,6 +8319,9 @@ setconfvalue(const char *selector, const char *value, int flags)
        case SWM_S_WINDOW_NAME_ENABLED:
                window_name_enabled = (atoi(value) != 0);
                break;
+       case SWM_S_WORKSPACE_CLAMP:
+               workspace_clamp = (atoi(value) != 0);
+               break;
        case SWM_S_WORKSPACE_LIMIT:
                workspace_limit = atoi(value);
                if (workspace_limit > SWM_WS_MAX)
@@ -8540,6 +8632,7 @@ struct config_option configopt[] = {
        { "java_workaround",            setconfvalue,   SWM_S_JAVA_WORKAROUND },
        { "keyboard_mapping",           setkeymapping,  0 },
        { "layout",                     setlayout,      0 },
+       { "maximize_hide_bar",          setconfvalue,   SWM_S_MAXIMIZE_HIDE_BAR },
        { "modkey",                     setconfmodkey,  0 },
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
@@ -8557,10 +8650,12 @@ struct config_option configopt[] = {
        { "urgent_collapse",            setconfvalue,   SWM_S_URGENT_COLLAPSE },
        { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
        { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
+       { "warp_focus",                 setconfvalue,   SWM_S_WARP_FOCUS },
        { "warp_pointer",               setconfvalue,   SWM_S_WARP_POINTER },
        { "window_class_enabled",       setconfvalue,   SWM_S_WINDOW_CLASS_ENABLED },
        { "window_instance_enabled",    setconfvalue,   SWM_S_WINDOW_INSTANCE_ENABLED },
        { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
+       { "workspace_clamp",            setconfvalue,   SWM_S_WORKSPACE_CLAMP },
        { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
        { "name",                       setconfvalue,   SWM_S_WORKSPACE_NAME },
 };
@@ -10691,10 +10786,16 @@ shutdown_cleanup(void)
 
        cursors_cleanup();
 
+       clear_quirks();
+       clear_spawns();
+       clear_keys();
+
        teardown_ewmh();
 
        num_screens = get_screen_count();
        for (i = 0; i < num_screens; ++i) {
+               int j;
+
                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                    screens[i].root, XCB_CURRENT_TIME);
 
@@ -10706,7 +10807,19 @@ shutdown_cleanup(void)
                        XftColorFree(display, DefaultVisual(display, i),
                            DefaultColormap(display, i), &search_font_color);
                }
+
+               for (j = 0; j < SWM_S_COLOR_MAX; ++j) {
+                       free(screens[i].c[j].name);
+               }
+
+               for (j = 0; j < SWM_WS_MAX; ++j) {
+                       free(screens[i].ws[j].name);
+               }
        }
+       free(screens);
+
+       free(bar_format);
+       free(clock_format);
 
        if (bar_font_legacy)
                XFreeFontSet(display, bar_fs);