@@ -7,6 +7,14 @@ static const unsigned int snap = 32; /* snap pixel */ | |||
static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ | |||
static const int showbar = 1; /* 0 means no bar */ | |||
static const int topbar = 1; /* 0 means bottom bar */ | |||
/* Display modes of the tab bar: never shown, always shown, shown only in */ | |||
/* monocle mode in presence of several windows. */ | |||
/* Modes after showtab_nmodes are disabled */ | |||
enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; | |||
static const int showtab = showtab_auto; /* Default tab bar show mode */ | |||
static const Bool toptab = False; /* False means bottom tab bar */ | |||
static const char *fonts[] = { "fontawesome:size=15", "DejaVuSansMono Nerd Font Mono:size=13"} ; | |||
static const char dmenufont[] = "monospace:size=13"; | |||
static const char col_gray1[] = "#222222"; | |||
@@ -22,27 +30,30 @@ static const char *colors[][3] = { | |||
static const char *const autostart[] = { | |||
"sh", "-c", "sh ~/scripts/xinit.sh", NULL, | |||
"dunst", NULL, | |||
"slstatus", NULL, | |||
NULL /* terminate */ | |||
}; | |||
/* tagging */ | |||
static const char *tags[] = { "", "", "", "", "", "", "", "", "" }; | |||
/* default layout per tags */ | |||
/* The first element is for all-tag view, following i-th element corresponds to */ | |||
/* tags[i]. Layout is referred using the layouts array index.*/ | |||
static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 2, 0, 2, 2}; | |||
static const Rule rules[] = { | |||
/* xprop(1): | |||
* WM_CLASS(STRING) = instance, class | |||
* WM_NAME(STRING) = title | |||
*/ | |||
/* class instance title tags mask iscentered isfloating monitor */ | |||
{ "Gimp", NULL, NULL, 0, 0, 1, -1 }, | |||
{ "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 }, | |||
/* class instance title tags mask iscentered isfloating isterminal noswallow monitor */ | |||
{ "Gimp", NULL, NULL, 0,0, 1, 0, 0, -1 }, | |||
{ "Firefox", NULL, NULL, 1 << 8,0, 0, 0, -1, -1 }, | |||
{ "St", NULL, NULL, 0,0, 0, 1, 0, -1 }, | |||
{ NULL, NULL, "Event Tester", 0,0, 0, 0, 1, -1 }, /* xev */ | |||
/* class instance title tags mask iscentered isfloating isterminal noswallow monitor */ | |||
{ "Gimp", NULL, NULL, 1 << 7, 0, 1, 0, 0, -1 }, | |||
{ "LibreWolf", NULL, NULL, 1 << 0, 0, 0, 0, -1, -1 }, | |||
{ "firefox", NULL, NULL, 1 << 0, 0, 0, 0, -1, -1 }, | |||
{ "St", NULL, NULL, 0, 0, 0, 1, 0, -1 }, | |||
{ NULL, NULL, "Event Tester", 0, 0, 0, 0, 1, -1 }, /* xev */ | |||
}; | |||
/* layout(s) */ | |||
@@ -82,8 +93,8 @@ static Key keys[] = { | |||
{ 0, XK_Print, spawn, SHCMD("~/scripts/screenshot.sh") }, | |||
{ 0, XF86XK_PowerOff, spawn, SHCMD("~/scripts/poweroff.sh") }, | |||
{ 0, XF86XK_AudioMute, spawn, SHCMD("pulsemixer --toggle-mute") }, | |||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("pulsemixer --change-volume +1") }, | |||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("pulsemixer --change-volume -1") }, | |||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("amixer -c 1 sset Master 1+") }, | |||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("amixer -c 1 sset Master 1-") }, | |||
{ 0, XF86XK_AudioPrev, spawn, SHCMD("cmus-remote --prev") }, | |||
{ 0, XF86XK_AudioNext, spawn, SHCMD("cmus-remote --next") }, | |||
{ 0, XF86XK_AudioPause, spawn, SHCMD("cmus-remote --pause") }, | |||
@@ -104,6 +115,7 @@ static Key keys[] = { | |||
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, | |||
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, | |||
{ MODKEY, XK_b, togglebar, {0} }, | |||
{ MODKEY, XK_w, tabmode, {-1} }, | |||
{ MODKEY, XK_j, focusstack, {.i = +1 } }, | |||
{ MODKEY, XK_k, focusstack, {.i = -1 } }, | |||
{ MODKEY, XK_i, incnmaster, {.i = +1 } }, | |||
@@ -156,5 +168,6 @@ static Button buttons[] = { | |||
{ ClkTagBar, 0, Button3, toggleview, {0} }, | |||
{ ClkTagBar, MODKEY, Button1, tag, {0} }, | |||
{ ClkTagBar, MODKEY, Button3, toggletag, {0} }, | |||
{ ClkTabBar, 0, Button1, focuswin, {0} }, | |||
}; | |||
@@ -0,0 +1,160 @@ | |||
/* See LICENSE file for copyright and license details. */ | |||
/* appearance */ | |||
static const unsigned int borderpx = 2; /* border pixel of windows */ | |||
static const unsigned int gappx = 15; | |||
static const unsigned int snap = 32; /* snap pixel */ | |||
static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ | |||
static const int showbar = 1; /* 0 means no bar */ | |||
static const int topbar = 1; /* 0 means bottom bar */ | |||
static const char *fonts[] = { "fontawesome:size=15", "DejaVuSansMono Nerd Font Mono:size=13"} ; | |||
static const char dmenufont[] = "monospace:size=13"; | |||
static const char col_gray1[] = "#222222"; | |||
static const char col_gray2[] = "#444444"; | |||
static const char col_gray3[] = "#bbbbbb"; | |||
static const char col_gray4[] = "#eeeeee"; | |||
static const char col_cyan[] = "#41008c"; | |||
static const char *colors[][3] = { | |||
/* fg bg border */ | |||
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, | |||
[SchemeSel] = { col_gray4, col_cyan, col_cyan }, | |||
}; | |||
static const char *const autostart[] = { | |||
"sh", "-c", "sh ~/scripts/xinit.sh", NULL, | |||
"dunst", NULL, | |||
"slstatus", NULL, | |||
NULL /* terminate */ | |||
}; | |||
/* tagging */ | |||
static const char *tags[] = { "", "", "", "", "", "", "", "", "" }; | |||
static const Rule rules[] = { | |||
/* xprop(1): | |||
* WM_CLASS(STRING) = instance, class | |||
* WM_NAME(STRING) = title | |||
*/ | |||
/* class instance title tags mask iscentered isfloating monitor */ | |||
{ "Gimp", NULL, NULL, 0, 0, 1, -1 }, | |||
{ "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 }, | |||
/* class instance title tags mask iscentered isfloating isterminal noswallow monitor */ | |||
{ "Gimp", NULL, NULL, 0,0, 1, 0, 0, -1 }, | |||
{ "Firefox", NULL, NULL, 1 << 8,0, 0, 0, -1, -1 }, | |||
{ "St", NULL, NULL, 0,0, 0, 1, 0, -1 }, | |||
{ NULL, NULL, "Event Tester", 0,0, 0, 0, 1, -1 }, /* xev */ | |||
}; | |||
/* layout(s) */ | |||
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ | |||
static const int nmaster = 1; /* number of clients in master area */ | |||
static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ | |||
static const Layout layouts[] = { | |||
/* symbol arrange function */ | |||
{ "[]=", tile }, /* first entry is default */ | |||
{ "><>", NULL }, /* no layout function means floating behavior */ | |||
{ "[M]", monocle }, | |||
{ "|M|", centeredmaster }, | |||
{ ">M>", centeredfloatingmaster }, | |||
}; | |||
/* key definitions */ | |||
#define MODKEY Mod4Mask | |||
#define TAGKEYS(KEY,TAG) \ | |||
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \ | |||
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ | |||
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ | |||
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, | |||
/* helper for spawning shell commands in the pre dwm-5.0 fashion */ | |||
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } | |||
/* commands */ | |||
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ | |||
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; | |||
static const char *termcmd[] = { "st", NULL }; | |||
#include <X11/XF86keysym.h> | |||
static Key keys[] = { | |||
/* modifier key function argument */ | |||
{ 0, XK_Print, spawn, SHCMD("~/scripts/screenshot.sh") }, | |||
{ 0, XF86XK_PowerOff, spawn, SHCMD("~/scripts/poweroff.sh") }, | |||
{ 0, XF86XK_AudioMute, spawn, SHCMD("pulsemixer --toggle-mute") }, | |||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("amixer -c 1 sset Master 1+") }, | |||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("amixer -c 1 sset Master 1-") }, | |||
{ 0, XF86XK_AudioPrev, spawn, SHCMD("cmus-remote --prev") }, | |||
{ 0, XF86XK_AudioNext, spawn, SHCMD("cmus-remote --next") }, | |||
{ 0, XF86XK_AudioPause, spawn, SHCMD("cmus-remote --pause") }, | |||
{ 0, XF86XK_AudioPlay, spawn, SHCMD("cmus-remote --pause") }, | |||
{ 0, XF86XK_AudioStop, spawn, SHCMD("cmus-remote --stop") }, | |||
{ 0, XF86XK_AudioRewind, spawn, SHCMD("cmus-remote --seek -10") }, | |||
{ 0, XF86XK_AudioForward, spawn, SHCMD("cmus-remote --seek +10") }, | |||
{ 0, XF86XK_MonBrightnessUp, spawn, SHCMD("brightnessctl s +5%")}, | |||
{ 0, XF86XK_MonBrightnessDown, spawn, SHCMD("brightnessctl s 5%-")}, | |||
{ MODKEY|ShiftMask, XK_space, togglealwaysontop, {0} }, | |||
{ MODKEY, XK_w, spawn, SHCMD("firefox") }, | |||
{ MODKEY|ShiftMask, XK_l, spawn, SHCMD("slock") }, | |||
{ MODKEY|ShiftMask, XK_m, spawn, SHCMD("st ~/scripts/autostart.sh") }, | |||
{ MODKEY|ShiftMask, XK_j, spawn, SHCMD("joplin-desktop") }, | |||
{ MODKEY|ShiftMask, XK_s, spawn, SHCMD("~/scripts/screenshot.sh 1") }, | |||
{ MODKEY|ShiftMask, XK_n, spawn, SHCMD("connman-gtk") }, | |||
{ MODKEY|ShiftMask, XK_x, spawn, SHCMD("mixer.sh") }, | |||
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, | |||
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, | |||
{ MODKEY, XK_b, togglebar, {0} }, | |||
{ MODKEY, XK_j, focusstack, {.i = +1 } }, | |||
{ MODKEY, XK_k, focusstack, {.i = -1 } }, | |||
{ MODKEY, XK_i, incnmaster, {.i = +1 } }, | |||
{ MODKEY, XK_d, incnmaster, {.i = -1 } }, | |||
{ MODKEY, XK_h, setmfact, {.f = -0.05} }, | |||
{ MODKEY, XK_l, setmfact, {.f = +0.05} }, | |||
{ MODKEY, XK_Return, zoom, {0} }, | |||
{ MODKEY, XK_Tab, view, {0} }, | |||
{ MODKEY|ShiftMask, XK_c, killclient, {0} }, | |||
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, | |||
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, | |||
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, | |||
{ MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, | |||
{ MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, | |||
{ MODKEY, XK_space, setlayout, {0} }, | |||
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} }, | |||
{ MODKEY, XK_0, view, {.ui = ~0 } }, | |||
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, | |||
{ MODKEY, XK_comma, focusmon, {.i = -1 } }, | |||
{ MODKEY, XK_period, focusmon, {.i = +1 } }, | |||
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, | |||
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, | |||
{ MODKEY, XK_minus, setgaps, {.i = -5 } }, | |||
{ MODKEY, XK_equal, setgaps, {.i = +5 } }, | |||
{ MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } }, | |||
TAGKEYS( XK_1, 0) | |||
TAGKEYS( XK_2, 1) | |||
TAGKEYS( XK_3, 2) | |||
TAGKEYS( XK_4, 3) | |||
TAGKEYS( XK_5, 4) | |||
TAGKEYS( XK_6, 5) | |||
TAGKEYS( XK_7, 6) | |||
TAGKEYS( XK_8, 7) | |||
TAGKEYS( XK_9, 8) | |||
{ MODKEY|ShiftMask, XK_q, quit, {0} }, | |||
}; | |||
/* button definitions */ | |||
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ | |||
static Button buttons[] = { | |||
/* click event mask button function argument */ | |||
{ ClkLtSymbol, 0, Button1, setlayout, {0} }, | |||
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, | |||
{ ClkWinTitle, 0, Button2, zoom, {0} }, | |||
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, | |||
{ ClkClientWin, MODKEY, Button1, movemouse, {0} }, | |||
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} }, | |||
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} }, | |||
{ ClkTagBar, 0, Button1, view, {0} }, | |||
{ ClkTagBar, 0, Button3, toggleview, {0} }, | |||
{ ClkTagBar, MODKEY, Button1, tag, {0} }, | |||
{ ClkTagBar, MODKEY, Button3, toggletag, {0} }, | |||
}; | |||
@@ -0,0 +1,33 @@ | |||
--- config.def.h | |||
+++ config.def.h | |||
@@ -5,6 +5,14 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ | |||
static const unsigned int snap = 32; /* snap pixel */ | |||
static const int showbar = 1; /* 0 means no bar */ | |||
static const int topbar = 1; /* 0 means bottom bar */ | |||
+ | |||
+/* Display modes of the tab bar: never shown, always shown, shown only in */ | |||
+/* monocle mode in presence of several windows. */ | |||
+/* Modes after showtab_nmodes are disabled */ | |||
+enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; | |||
+static const int showtab = showtab_auto; /* Default tab bar show mode */ | |||
+static const Bool toptab = False; /* False means bottom tab bar */ | |||
+ | |||
static const char *fonts[] = { "monospace:size=10" }; | |||
static const char dmenufont[] = "monospace:size=10"; | |||
static const char col_gray1[] = "#222222"; | |||
@@ -18,9 +26,15 @@ static const char *colors[SchemeLast][3] = { | |||
[SchemeSel] = { col_gray4, col_cyan, col_cyan }, | |||
}; | |||
+ | |||
/* tagging */ | |||
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; | |||
+/* default layout per tags */ | |||
+/* The first element is for all-tag view, following i-th element corresponds to */ | |||
+/* tags[i]. Layout is referred using the layouts array index.*/ | |||
+static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |||
+ | |||
static const Rule rules[] = { | |||
/* xprop(1): | |||
* WM_CLASS(STRING) = instance, class |
@@ -7,6 +7,14 @@ static const unsigned int snap = 32; /* snap pixel */ | |||
static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ | |||
static const int showbar = 1; /* 0 means no bar */ | |||
static const int topbar = 1; /* 0 means bottom bar */ | |||
/* Display modes of the tab bar: never shown, always shown, shown only in */ | |||
/* monocle mode in presence of several windows. */ | |||
/* Modes after showtab_nmodes are disabled */ | |||
enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; | |||
static const int showtab = showtab_auto; /* Default tab bar show mode */ | |||
static const Bool toptab = False; /* False means bottom tab bar */ | |||
static const char *fonts[] = { "fontawesome:size=15", "DejaVuSansMono Nerd Font Mono:size=13"} ; | |||
static const char dmenufont[] = "monospace:size=13"; | |||
static const char col_gray1[] = "#222222"; | |||
@@ -22,27 +30,30 @@ static const char *colors[][3] = { | |||
static const char *const autostart[] = { | |||
"sh", "-c", "sh ~/scripts/xinit.sh", NULL, | |||
"dunst", NULL, | |||
"slstatus", NULL, | |||
NULL /* terminate */ | |||
}; | |||
/* tagging */ | |||
static const char *tags[] = { "", "", "", "", "", "", "", "", "" }; | |||
/* default layout per tags */ | |||
/* The first element is for all-tag view, following i-th element corresponds to */ | |||
/* tags[i]. Layout is referred using the layouts array index.*/ | |||
static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 2, 0, 2, 2}; | |||
static const Rule rules[] = { | |||
/* xprop(1): | |||
* WM_CLASS(STRING) = instance, class | |||
* WM_NAME(STRING) = title | |||
*/ | |||
/* class instance title tags mask iscentered isfloating monitor */ | |||
{ "Gimp", NULL, NULL, 0, 0, 1, -1 }, | |||
{ "Firefox", NULL, NULL, 1 << 8, 0, 0, -1 }, | |||
/* class instance title tags mask iscentered isfloating isterminal noswallow monitor */ | |||
{ "Gimp", NULL, NULL, 0,0, 1, 0, 0, -1 }, | |||
{ "Firefox", NULL, NULL, 1 << 8,0, 0, 0, -1, -1 }, | |||
{ "St", NULL, NULL, 0,0, 0, 1, 0, -1 }, | |||
{ NULL, NULL, "Event Tester", 0,0, 0, 0, 1, -1 }, /* xev */ | |||
/* class instance title tags mask iscentered isfloating isterminal noswallow monitor */ | |||
{ "Gimp", NULL, NULL, 1 << 7, 0, 1, 0, 0, -1 }, | |||
{ "LibreWolf", NULL, NULL, 1 << 0, 0, 0, 0, -1, -1 }, | |||
{ "firefox", NULL, NULL, 1 << 0, 0, 0, 0, -1, -1 }, | |||
{ "St", NULL, NULL, 0, 0, 0, 1, 0, -1 }, | |||
{ NULL, NULL, "Event Tester", 0, 0, 0, 0, 1, -1 }, /* xev */ | |||
}; | |||
/* layout(s) */ | |||
@@ -82,8 +93,8 @@ static Key keys[] = { | |||
{ 0, XK_Print, spawn, SHCMD("~/scripts/screenshot.sh") }, | |||
{ 0, XF86XK_PowerOff, spawn, SHCMD("~/scripts/poweroff.sh") }, | |||
{ 0, XF86XK_AudioMute, spawn, SHCMD("pulsemixer --toggle-mute") }, | |||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("pulsemixer --change-volume +1") }, | |||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("pulsemixer --change-volume -1") }, | |||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("amixer -c 1 sset Master 1+") }, | |||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("amixer -c 1 sset Master 1-") }, | |||
{ 0, XF86XK_AudioPrev, spawn, SHCMD("cmus-remote --prev") }, | |||
{ 0, XF86XK_AudioNext, spawn, SHCMD("cmus-remote --next") }, | |||
{ 0, XF86XK_AudioPause, spawn, SHCMD("cmus-remote --pause") }, | |||
@@ -104,6 +115,7 @@ static Key keys[] = { | |||
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, | |||
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, | |||
{ MODKEY, XK_b, togglebar, {0} }, | |||
{ MODKEY, XK_w, tabmode, {-1} }, | |||
{ MODKEY, XK_j, focusstack, {.i = +1 } }, | |||
{ MODKEY, XK_k, focusstack, {.i = -1 } }, | |||
{ MODKEY, XK_i, incnmaster, {.i = +1 } }, | |||
@@ -156,5 +168,6 @@ static Button buttons[] = { | |||
{ ClkTagBar, 0, Button3, toggleview, {0} }, | |||
{ ClkTagBar, MODKEY, Button1, tag, {0} }, | |||
{ ClkTagBar, MODKEY, Button3, toggletag, {0} }, | |||
{ ClkTabBar, 0, Button1, focuswin, {0} }, | |||
}; | |||
@@ -0,0 +1,808 @@ | |||
diff --git a/config.def.h b/config.def.h | |||
index fd77a07..8cc91c0 100644 | |||
--- a/config.def.h | |||
+++ b/config.def.h | |||
@@ -5,6 +5,14 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ | |||
static const unsigned int snap = 32; /* snap pixel */ | |||
static const int showbar = 1; /* 0 means no bar */ | |||
static const int topbar = 1; /* 0 means bottom bar */ | |||
+ | |||
+/* Display modes of the tab bar: never shown, always shown, shown only in */ | |||
+/* monocle mode in presence of several windows. */ | |||
+/* Modes after showtab_nmodes are disabled */ | |||
+enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always}; | |||
+static const int showtab = showtab_auto; /* Default tab bar show mode */ | |||
+static const Bool toptab = False; /* False means bottom tab bar */ | |||
+ | |||
static const char *fonts[] = { "monospace:size=10" }; | |||
static const char dmenufont[] = "monospace:size=10"; | |||
static const char col_gray1[] = "#222222"; | |||
@@ -18,9 +26,15 @@ static const char *colors[SchemeLast][3] = { | |||
[SchemeSel] = { col_gray4, col_cyan, col_cyan }, | |||
}; | |||
+ | |||
/* tagging */ | |||
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; | |||
+/* default layout per tags */ | |||
+/* The first element is for all-tag view, following i-th element corresponds to */ | |||
+/* tags[i]. Layout is referred using the layouts array index.*/ | |||
+static int def_layouts[1 + LENGTH(tags)] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |||
+ | |||
static const Rule rules[] = { | |||
/* xprop(1): | |||
* WM_CLASS(STRING) = instance, class | |||
@@ -64,6 +78,7 @@ static Key keys[] = { | |||
{ MODKEY, XK_p, spawn, {.v = dmenucmd } }, | |||
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, | |||
{ MODKEY, XK_b, togglebar, {0} }, | |||
+ { MODKEY, XK_w, tabmode, {-1} }, | |||
{ MODKEY, XK_j, focusstack, {.i = +1 } }, | |||
{ MODKEY, XK_k, focusstack, {.i = -1 } }, | |||
{ MODKEY, XK_i, incnmaster, {.i = +1 } }, | |||
@@ -111,5 +126,6 @@ static Button buttons[] = { | |||
{ ClkTagBar, 0, Button3, toggleview, {0} }, | |||
{ ClkTagBar, MODKEY, Button1, tag, {0} }, | |||
{ ClkTagBar, MODKEY, Button3, toggletag, {0} }, | |||
+ { ClkTabBar, 0, Button1, focuswin, {0} }, | |||
}; | |||
diff --git a/dwm.1 b/dwm.1 | |||
index 6687011..077d92b 100644 | |||
--- a/dwm.1 | |||
+++ b/dwm.1 | |||
@@ -19,14 +19,22 @@ layout applied. | |||
Windows are grouped by tags. Each window can be tagged with one or multiple | |||
tags. Selecting certain tags displays all windows with these tags. | |||
.P | |||
-Each screen contains a small status bar which displays all available tags, the | |||
-layout, the title of the focused window, and the text read from the root window | |||
-name property, if the screen is focused. A floating window is indicated with an | |||
-empty square and a maximised floating window is indicated with a filled square | |||
-before the windows title. The selected tags are indicated with a different | |||
-color. The tags of the focused window are indicated with a filled square in the | |||
-top left corner. The tags which are applied to one or more windows are | |||
-indicated with an empty square in the top left corner. | |||
+Each screen contains two small status bars. | |||
+.P | |||
+One bar displays all available tags, the layout, the title of the focused | |||
+window, and the text read from the root window name property, if the screen is | |||
+focused. A floating window is indicated with an empty square and a maximised | |||
+floating window is indicated with a filled square before the windows title. The | |||
+selected tags are indicated with a different color. The tags of the focused | |||
+window are indicated with a filled square in the top left corner. The tags | |||
+which are applied to one or more windows are indicated with an empty square in | |||
+the top left corner. | |||
+.P | |||
+Another bar contains a tab for each window of the current view and allows | |||
+navigation between windows, especially in the monocle mode. The different | |||
+display modes of this bar are described under the Mod1\-w Keybord command | |||
+section. When a single tag is selected, that tag is indicated in the left corner | |||
+of the tab bar. | |||
.P | |||
dwm draws a small border around windows to indicate the focus state. | |||
.SH OPTIONS | |||
@@ -43,7 +51,8 @@ command. | |||
.TP | |||
.B Button1 | |||
click on a tag label to display all windows with that tag, click on the layout | |||
-label toggles between tiled and floating layout. | |||
+label toggles between tiled and floating layout, click on a window name in the | |||
+tab bar brings focus to that window. | |||
.TP | |||
.B Button3 | |||
click on a tag label adds/removes all windows with that tag to/from the view. | |||
@@ -104,6 +113,12 @@ Increase master area size. | |||
.B Mod1\-h | |||
Decrease master area size. | |||
.TP | |||
+.B Mod1\-w | |||
+Cycle over the tab bar display modes: never displayed, always displayed, | |||
+displayed only in monocle mode when the view contains than one window (auto | |||
+mode). Some display modes can be disabled in the configuration, config.h. In | |||
+the default configuration only "never" and "auto" display modes are enabled. | |||
+.TP | |||
.B Mod1\-Return | |||
Zooms/cycles focused window to/from master area (tiled layouts only). | |||
.TP | |||
diff --git a/dwm.c b/dwm.c | |||
index b2bc9bd..0c34020 100644 | |||
--- a/dwm.c | |||
+++ b/dwm.c | |||
@@ -65,7 +65,7 @@ enum { NetSupported, NetWMName, NetWMState, | |||
NetWMFullscreen, NetActiveWindow, NetWMWindowType, | |||
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ | |||
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ | |||
-enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, | |||
+enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, | |||
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ | |||
typedef union { | |||
@@ -112,25 +112,35 @@ typedef struct { | |||
void (*arrange)(Monitor *); | |||
} Layout; | |||
+#define MAXTABS 50 | |||
+ | |||
+typedef struct Pertag Pertag; | |||
struct Monitor { | |||
char ltsymbol[16]; | |||
float mfact; | |||
int nmaster; | |||
int num; | |||
int by; /* bar geometry */ | |||
+ int ty; /* tab bar geometry */ | |||
int mx, my, mw, mh; /* screen size */ | |||
int wx, wy, ww, wh; /* window area */ | |||
unsigned int seltags; | |||
unsigned int sellt; | |||
unsigned int tagset[2]; | |||
int showbar; | |||
+ int showtab; | |||
int topbar; | |||
+ int toptab; | |||
Client *clients; | |||
Client *sel; | |||
Client *stack; | |||
Monitor *next; | |||
Window barwin; | |||
+ Window tabwin; | |||
+ int ntabs; | |||
+ int tab_widths[MAXTABS]; | |||
const Layout *lt[2]; | |||
+ Pertag *pertag; | |||
}; | |||
typedef struct { | |||
@@ -165,12 +175,15 @@ static void detachstack(Client *c); | |||
static Monitor *dirtomon(int dir); | |||
static void drawbar(Monitor *m); | |||
static void drawbars(void); | |||
+static void drawtab(Monitor *m); | |||
+static void drawtabs(void); | |||
static void enternotify(XEvent *e); | |||
static void expose(XEvent *e); | |||
static void focus(Client *c); | |||
static void focusin(XEvent *e); | |||
static void focusmon(const Arg *arg); | |||
static void focusstack(const Arg *arg); | |||
+static void focuswin(const Arg* arg); | |||
static int getrootptr(int *x, int *y); | |||
static long getstate(Window w); | |||
static int gettextprop(Window w, Atom atom, char *text, unsigned int size); | |||
@@ -207,6 +220,7 @@ static void setup(void); | |||
static void showhide(Client *c); | |||
static void sigchld(int unused); | |||
static void spawn(const Arg *arg); | |||
+static void tabmode(const Arg *arg); | |||
static void tag(const Arg *arg); | |||
static void tagmon(const Arg *arg); | |||
static void tile(Monitor *); | |||
@@ -241,6 +255,7 @@ static char stext[256]; | |||
static int screen; | |||
static int sw, sh; /* X display screen geometry width, height */ | |||
static int bh, blw = 0; /* bar geometry */ | |||
+static int th = 0; /* tab bar geometry */ | |||
static int lrpad; /* sum of left and right padding for text */ | |||
static int (*xerrorxlib)(Display *, XErrorEvent *); | |||
static unsigned int numlockmask = 0; | |||
@@ -272,6 +287,16 @@ static Window root; | |||
/* configuration, allows nested code to access above variables */ | |||
#include "config.h" | |||
+struct Pertag { | |||
+ unsigned int curtag, prevtag; /* current and previous tag */ | |||
+ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ | |||
+ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ | |||
+ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ | |||
+ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ | |||
+ Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ | |||
+ Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ | |||
+}; | |||
+ | |||
/* compile-time check if all tags fit into an unsigned int bit array. */ | |||
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; | |||
@@ -395,6 +420,8 @@ arrange(Monitor *m) | |||
void | |||
arrangemon(Monitor *m) | |||
{ | |||
+ updatebarpos(m); | |||
+ XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); | |||
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); | |||
if (m->lt[m->sellt]->arrange) | |||
m->lt[m->sellt]->arrange(m); | |||
@@ -444,14 +471,33 @@ buttonpress(XEvent *e) | |||
click = ClkStatusText; | |||
else | |||
click = ClkWinTitle; | |||
- } else if ((c = wintoclient(ev->window))) { | |||
+ } | |||
+ if(ev->window == selmon->tabwin) { | |||
+ i = 0; x = 0; | |||
+ for(c = selmon->clients; c; c = c->next){ | |||
+ if(!ISVISIBLE(c)) continue; | |||
+ x += selmon->tab_widths[i]; | |||
+ if (ev->x > x) | |||
+ ++i; | |||
+ else | |||
+ break; | |||
+ if(i >= m->ntabs) break; | |||
+ } | |||
+ if(c) { | |||
+ click = ClkTabBar; | |||
+ arg.ui = i; | |||
+ } | |||
+ } | |||
+ else if((c = wintoclient(ev->window))) { | |||
focus(c); | |||
click = ClkClientWin; | |||
} | |||
- for (i = 0; i < LENGTH(buttons); i++) | |||
- if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | |||
- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) | |||
- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); | |||
+ for(i = 0; i < LENGTH(buttons); i++) | |||
+ if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | |||
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ | |||
+ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) | |||
+ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); | |||
+ } | |||
} | |||
void | |||
@@ -476,8 +522,8 @@ cleanup(void) | |||
view(&a); | |||
selmon->lt[selmon->sellt] = &foo; | |||
for (m = mons; m; m = m->next) | |||
- while (m->stack) | |||
- unmanage(m->stack, 0); | |||
+ while(m->stack) | |||
+ unmanage(m->stack, False); | |||
XUngrabKey(dpy, AnyKey, AnyModifier, root); | |||
while (mons) | |||
cleanupmon(mons); | |||
@@ -504,6 +550,8 @@ cleanupmon(Monitor *mon) | |||
} | |||
XUnmapWindow(dpy, mon->barwin); | |||
XDestroyWindow(dpy, mon->barwin); | |||
+ XUnmapWindow(dpy, mon->tabwin); | |||
+ XDestroyWindow(dpy, mon->tabwin); | |||
free(mon); | |||
} | |||
@@ -525,6 +573,7 @@ clientmessage(XEvent *e) | |||
{ | |||
XClientMessageEvent *cme = &e->xclient; | |||
Client *c = wintoclient(cme->window); | |||
+ int i; | |||
if (!c) | |||
return; | |||
@@ -536,6 +585,8 @@ clientmessage(XEvent *e) | |||
if (!ISVISIBLE(c)) { | |||
c->mon->seltags ^= 1; | |||
c->mon->tagset[c->mon->seltags] = c->tags; | |||
+ for(i=0; !(c->tags & 1 << i); i++); | |||
+ view(&(Arg){.ui = 1 << i}); | |||
} | |||
pop(c); | |||
} | |||
@@ -564,11 +615,10 @@ void | |||
configurenotify(XEvent *e) | |||
{ | |||
Monitor *m; | |||
- Client *c; | |||
XConfigureEvent *ev = &e->xconfigure; | |||
int dirty; | |||
- /* TODO: updategeom handling sucks, needs to be simplified */ | |||
+ // TODO: updategeom handling sucks, needs to be simplified | |||
if (ev->window == root) { | |||
dirty = (sw != ev->width || sh != ev->height); | |||
sw = ev->width; | |||
@@ -576,10 +626,9 @@ configurenotify(XEvent *e) | |||
if (updategeom() || dirty) { | |||
drw_resize(drw, sw, bh); | |||
updatebars(); | |||
- for (m = mons; m; m = m->next) { | |||
- for (c = m->clients; c; c = c->next) | |||
- if (c->isfullscreen) | |||
- resizeclient(c, m->mx, m->my, m->mw, m->mh); | |||
+ //refreshing display of status bar. The tab bar is handled by the arrange() | |||
+ //method, which is called below | |||
+ for (m = mons; m; m = m->next){ | |||
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); | |||
} | |||
focus(NULL); | |||
@@ -644,16 +693,41 @@ Monitor * | |||
createmon(void) | |||
{ | |||
Monitor *m; | |||
+ int i; | |||
m = ecalloc(1, sizeof(Monitor)); | |||
m->tagset[0] = m->tagset[1] = 1; | |||
m->mfact = mfact; | |||
m->nmaster = nmaster; | |||
m->showbar = showbar; | |||
+ m->showtab = showtab; | |||
m->topbar = topbar; | |||
- m->lt[0] = &layouts[0]; | |||
+ m->toptab = toptab; | |||
+ m->ntabs = 0; | |||
+ m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)]; | |||
m->lt[1] = &layouts[1 % LENGTH(layouts)]; | |||
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); | |||
+ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) | |||
+ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); | |||
+ m->pertag->curtag = m->pertag->prevtag = 1; | |||
+ for(i=0; i <= LENGTH(tags); i++) { | |||
+ /* init nmaster */ | |||
+ m->pertag->nmasters[i] = m->nmaster; | |||
+ | |||
+ /* init mfacts */ | |||
+ m->pertag->mfacts[i] = m->mfact; | |||
+ | |||
+ /* init layouts */ | |||
+ m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)]; | |||
+ m->pertag->ltidxs[i][1] = m->lt[1]; | |||
+ m->pertag->sellts[i] = m->sellt; | |||
+ | |||
+ /* init showbar */ | |||
+ m->pertag->showbars[i] = m->showbar; | |||
+ | |||
+ /* swap focus and zoomswap*/ | |||
+ m->pertag->prevzooms[i] = NULL; | |||
+ } | |||
return m; | |||
} | |||
@@ -765,6 +839,104 @@ drawbars(void) | |||
} | |||
void | |||
+drawtabs(void) { | |||
+ Monitor *m; | |||
+ | |||
+ for(m = mons; m; m = m->next) | |||
+ drawtab(m); | |||
+} | |||
+ | |||
+static int | |||
+cmpint(const void *p1, const void *p2) { | |||
+ /* The actual arguments to this function are "pointers to | |||
+ pointers to char", but strcmp(3) arguments are "pointers | |||
+ to char", hence the following cast plus dereference */ | |||
+ return *((int*) p1) > * (int*) p2; | |||
+} | |||
+ | |||
+ | |||
+void | |||
+drawtab(Monitor *m) { | |||
+ Client *c; | |||
+ int i; | |||
+ int itag = -1; | |||
+ char view_info[50]; | |||
+ int view_info_w = 0; | |||
+ int sorted_label_widths[MAXTABS]; | |||
+ int tot_width; | |||
+ int maxsize = bh; | |||
+ int x = 0; | |||
+ int w = 0; | |||
+ | |||
+ //view_info: indicate the tag which is displayed in the view | |||
+ for(i = 0; i < LENGTH(tags); ++i){ | |||
+ if((selmon->tagset[selmon->seltags] >> i) & 1) { | |||
+ if(itag >=0){ //more than one tag selected | |||
+ itag = -1; | |||
+ break; | |||
+ } | |||
+ itag = i; | |||
+ } | |||
+ } | |||
+ if(0 <= itag && itag < LENGTH(tags)){ | |||
+ snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); | |||
+ } else { | |||
+ strncpy(view_info, "[...]", sizeof view_info); | |||
+ } | |||
+ view_info[sizeof(view_info) - 1 ] = 0; | |||
+ view_info_w = TEXTW(view_info); | |||
+ tot_width = view_info_w; | |||
+ | |||
+ /* Calculates number of labels and their width */ | |||
+ m->ntabs = 0; | |||
+ for(c = m->clients; c; c = c->next){ | |||
+ if(!ISVISIBLE(c)) continue; | |||
+ m->tab_widths[m->ntabs] = TEXTW(c->name); | |||
+ tot_width += m->tab_widths[m->ntabs]; | |||
+ ++m->ntabs; | |||
+ if(m->ntabs >= MAXTABS) break; | |||
+ } | |||
+ | |||
+ if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated | |||
+ memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); | |||
+ qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); | |||
+ tot_width = view_info_w; | |||
+ for(i = 0; i < m->ntabs; ++i){ | |||
+ if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) | |||
+ break; | |||
+ tot_width += sorted_label_widths[i]; | |||
+ } | |||
+ maxsize = (m->ww - tot_width) / (m->ntabs - i); | |||
+ } else{ | |||
+ maxsize = m->ww; | |||
+ } | |||
+ i = 0; | |||
+ for(c = m->clients; c; c = c->next){ | |||
+ if(!ISVISIBLE(c)) continue; | |||
+ if(i >= m->ntabs) break; | |||
+ if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; | |||
+ w = m->tab_widths[i]; | |||
+ drw_setscheme(drw, (c == m->sel) ? scheme[SchemeSel] : scheme[SchemeNorm]); | |||
+ drw_text(drw, x, 0, w, th, lrpad / 2, c->name, 0); | |||
+ x += w; | |||
+ ++i; | |||
+ } | |||
+ | |||
+ drw_setscheme(drw, scheme[SchemeNorm]); | |||
+ | |||
+ /* cleans interspace between window names and current viewed tag label */ | |||
+ w = m->ww - view_info_w - x; | |||
+ drw_text(drw, x, 0, w, th, lrpad / 2, "", 0); | |||
+ | |||
+ /* view info */ | |||
+ x += w; | |||
+ w = view_info_w; | |||
+ drw_text(drw, x, 0, w, th, lrpad / 2, view_info, 0); | |||
+ | |||
+ drw_map(drw, m->tabwin, 0, 0, m->ww, th); | |||
+} | |||
+ | |||
+void | |||
enternotify(XEvent *e) | |||
{ | |||
Client *c; | |||
@@ -789,8 +961,10 @@ expose(XEvent *e) | |||
Monitor *m; | |||
XExposeEvent *ev = &e->xexpose; | |||
- if (ev->count == 0 && (m = wintomon(ev->window))) | |||
+ if (ev->count == 0 && (m = wintomon(ev->window))){ | |||
drawbar(m); | |||
+ drawtab(m); | |||
+ } | |||
} | |||
void | |||
@@ -817,6 +991,7 @@ focus(Client *c) | |||
} | |||
selmon->sel = c; | |||
drawbars(); | |||
+ drawtabs(); | |||
} | |||
/* there are some broken focus acquiring clients */ | |||
@@ -870,6 +1045,19 @@ focusstack(const Arg *arg) | |||
} | |||
} | |||
+void | |||
+focuswin(const Arg* arg){ | |||
+ int iwin = arg->i; | |||
+ Client* c = NULL; | |||
+ for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ | |||
+ if(ISVISIBLE(c)) --iwin; | |||
+ }; | |||
+ if(c) { | |||
+ focus(c); | |||
+ restack(selmon); | |||
+ } | |||
+} | |||
+ | |||
Atom | |||
getatomprop(Client *c, Atom prop) | |||
{ | |||
@@ -983,7 +1171,7 @@ grabkeys(void) | |||
void | |||
incnmaster(const Arg *arg) | |||
{ | |||
- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); | |||
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); | |||
arrange(selmon); | |||
} | |||
@@ -1142,7 +1330,7 @@ motionnotify(XEvent *e) | |||
if (ev->window != root) | |||
return; | |||
if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { | |||
- unfocus(selmon->sel, 1); | |||
+ unfocus(selmon->sel, True); | |||
selmon = m; | |||
focus(NULL); | |||
} | |||
@@ -1162,11 +1350,13 @@ movemouse(const Arg *arg) | |||
return; | |||
if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ | |||
return; | |||
+ if(c->isfullscreen) /* no support moving fullscreen windows by mouse */ | |||
+ return; | |||
restack(selmon); | |||
ocx = c->x; | |||
ocy = c->y; | |||
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | |||
- None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) | |||
+ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) | |||
return; | |||
if (!getrootptr(&x, &y)) | |||
return; | |||
@@ -1253,12 +1443,14 @@ propertynotify(XEvent *e) | |||
case XA_WM_HINTS: | |||
updatewmhints(c); | |||
drawbars(); | |||
+ drawtabs(); | |||
break; | |||
} | |||
if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { | |||
updatetitle(c); | |||
if (c == c->mon->sel) | |||
drawbar(c->mon); | |||
+ drawtab(c->mon); | |||
} | |||
if (ev->atom == netatom[NetWMWindowType]) | |||
updatewindowtype(c); | |||
@@ -1320,11 +1512,13 @@ resizemouse(const Arg *arg) | |||
return; | |||
if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ | |||
return; | |||
+ if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */ | |||
+ return; | |||
restack(selmon); | |||
ocx = c->x; | |||
ocy = c->y; | |||
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | |||
- None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) | |||
+ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) | |||
return; | |||
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); | |||
do { | |||
@@ -1372,6 +1566,7 @@ restack(Monitor *m) | |||
XWindowChanges wc; | |||
drawbar(m); | |||
+ drawtab(m); | |||
if (!m->sel) | |||
return; | |||
if (m->sel->isfloating || !m->lt[m->sellt]->arrange) | |||
@@ -1480,11 +1675,11 @@ sendevent(Client *c, Atom proto) | |||
void | |||
setfocus(Client *c) | |||
{ | |||
- if (!c->neverfocus) { | |||
+ if(!c->neverfocus) { | |||
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | |||
XChangeProperty(dpy, root, netatom[NetActiveWindow], | |||
- XA_WINDOW, 32, PropModeReplace, | |||
- (unsigned char *) &(c->win), 1); | |||
+ XA_WINDOW, 32, PropModeReplace, | |||
+ (unsigned char *) &(c->win), 1); | |||
} | |||
sendevent(c, wmatom[WMTakeFocus]); | |||
} | |||
@@ -1520,10 +1715,13 @@ setfullscreen(Client *c, int fullscreen) | |||
void | |||
setlayout(const Arg *arg) | |||
{ | |||
- if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) | |||
- selmon->sellt ^= 1; | |||
- if (arg && arg->v) | |||
- selmon->lt[selmon->sellt] = (Layout *)arg->v; | |||
+ if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { | |||
+ selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; | |||
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; | |||
+ } | |||
+ if(arg && arg->v) | |||
+ selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; | |||
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; | |||
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); | |||
if (selmon->sel) | |||
arrange(selmon); | |||
@@ -1542,7 +1740,7 @@ setmfact(const Arg *arg) | |||
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; | |||
if (f < 0.1 || f > 0.9) | |||
return; | |||
- selmon->mfact = f; | |||
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; | |||
arrange(selmon); | |||
} | |||
@@ -1564,6 +1762,7 @@ setup(void) | |||
die("no fonts could be loaded.\n"); | |||
lrpad = drw->fonts->h; | |||
bh = drw->fonts->h + 2; | |||
+ th = bh; | |||
updategeom(); | |||
/* init atoms */ | |||
wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); | |||
@@ -1631,10 +1830,10 @@ sigchld(int unused) | |||
void | |||
spawn(const Arg *arg) | |||
{ | |||
- if (arg->v == dmenucmd) | |||
+ if(arg->v == dmenucmd) | |||
dmenumon[0] = '0' + selmon->num; | |||
- if (fork() == 0) { | |||
- if (dpy) | |||
+ if(fork() == 0) { | |||
+ if(dpy) | |||
close(ConnectionNumber(dpy)); | |||
setsid(); | |||
execvp(((char **)arg->v)[0], (char **)arg->v); | |||
@@ -1691,18 +1890,29 @@ tile(Monitor *m) | |||
void | |||
togglebar(const Arg *arg) | |||
{ | |||
- selmon->showbar = !selmon->showbar; | |||
+ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; | |||
updatebarpos(selmon); | |||
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); | |||
arrange(selmon); | |||
} | |||
void | |||
+tabmode(const Arg *arg) | |||
+{ | |||
+ if(arg && arg->i >= 0) | |||
+ selmon->showtab = arg->ui % showtab_nmodes; | |||
+ else | |||
+ selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes; | |||
+ arrange(selmon); | |||
+} | |||
+ | |||
+ | |||
+void | |||
togglefloating(const Arg *arg) | |||
{ | |||
- if (!selmon->sel) | |||
+ if(!selmon->sel) | |||
return; | |||
- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ | |||
+ if(selmon->sel->isfullscreen) /* no support for fullscreen windows */ | |||
return; | |||
selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; | |||
if (selmon->sel->isfloating) | |||
@@ -1730,9 +1940,29 @@ void | |||
toggleview(const Arg *arg) | |||
{ | |||
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); | |||
+ int i; | |||
if (newtagset) { | |||
+ if(newtagset == ~0) { | |||
+ selmon->pertag->prevtag = selmon->pertag->curtag; | |||
+ selmon->pertag->curtag = 0; | |||
+ } | |||
+ /* test if the user did not select the same tag */ | |||
+ if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) { | |||
+ selmon->pertag->prevtag = selmon->pertag->curtag; | |||
+ for (i=0; !(newtagset & 1 << i); i++) ; | |||
+ selmon->pertag->curtag = i + 1; | |||
+ } | |||
selmon->tagset[selmon->seltags] = newtagset; | |||
+ | |||
+ /* apply settings for this view */ | |||
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; | |||
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; | |||
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; | |||
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; | |||
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; | |||
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) | |||
+ togglebar(NULL); | |||
focus(NULL); | |||
arrange(selmon); | |||
} | |||
@@ -1808,20 +2038,44 @@ updatebars(void) | |||
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | |||
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); | |||
XMapRaised(dpy, m->barwin); | |||
+ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), | |||
+ CopyFromParent, DefaultVisual(dpy, screen), | |||
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | |||
+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); | |||
+ XMapRaised(dpy, m->tabwin); | |||
} | |||
} | |||
void | |||
updatebarpos(Monitor *m) | |||
{ | |||
+ Client *c; | |||
+ int nvis = 0; | |||
+ | |||
m->wy = m->my; | |||
m->wh = m->mh; | |||
if (m->showbar) { | |||
m->wh -= bh; | |||
m->by = m->topbar ? m->wy : m->wy + m->wh; | |||
- m->wy = m->topbar ? m->wy + bh : m->wy; | |||
- } else | |||
+ if ( m->topbar ) | |||
+ m->wy += bh; | |||
+ } else { | |||
m->by = -bh; | |||
+ } | |||
+ | |||
+ for(c = m->clients; c; c = c->next){ | |||
+ if(ISVISIBLE(c)) ++nvis; | |||
+ } | |||
+ | |||
+ if(m->showtab == showtab_always | |||
+ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){ | |||
+ m->wh -= th; | |||
+ m->ty = m->toptab ? m->wy : m->wy + m->wh; | |||
+ if ( m->toptab ) | |||
+ m->wy += th; | |||
+ } else { | |||
+ m->ty = -th; | |||
+ } | |||
} | |||
void | |||
@@ -2003,9 +2257,9 @@ updatewindowtype(Client *c) | |||
Atom wtype = getatomprop(c, netatom[NetWMWindowType]); | |||
if (state == netatom[NetWMFullscreen]) | |||
- setfullscreen(c, 1); | |||
+ setfullscreen(c, True); | |||
if (wtype == netatom[NetWMWindowTypeDialog]) | |||
- c->isfloating = 1; | |||
+ c->isfloating = True; | |||
} | |||
void | |||
@@ -2030,11 +2284,33 @@ updatewmhints(Client *c) | |||
void | |||
view(const Arg *arg) | |||
{ | |||
- if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | |||
+ int i; | |||
+ unsigned int tmptag; | |||
+ | |||
+ if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | |||
return; | |||
selmon->seltags ^= 1; /* toggle sel tagset */ | |||
- if (arg->ui & TAGMASK) | |||
+ if(arg->ui & TAGMASK) { | |||
+ selmon->pertag->prevtag = selmon->pertag->curtag; | |||
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; | |||
+ if(arg->ui == ~0) | |||
+ selmon->pertag->curtag = 0; | |||
+ else { | |||
+ for (i=0; !(arg->ui & 1 << i); i++) ; | |||
+ selmon->pertag->curtag = i + 1; | |||
+ } | |||
+ } else { | |||
+ tmptag = selmon->pertag->prevtag; | |||
+ selmon->pertag->prevtag = selmon->pertag->curtag; | |||
+ selmon->pertag->curtag = tmptag; | |||
+ } | |||
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; | |||
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; | |||
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; | |||
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; | |||
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; | |||
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) | |||
+ togglebar(NULL); | |||
focus(NULL); | |||
arrange(selmon); | |||
} | |||
@@ -2062,7 +2338,7 @@ wintomon(Window w) | |||
if (w == root && getrootptr(&x, &y)) | |||
return recttomon(x, y, 1, 1); | |||
for (m = mons; m; m = m->next) | |||
- if (w == m->barwin) | |||
+ if(w == m->barwin || w == m->tabwin) | |||
return m; | |||
if ((c = wintoclient(w))) | |||
return c->mon; |
@@ -20,14 +20,22 @@ layout applied. | |||
Windows are grouped by tags. Each window can be tagged with one or multiple | |||
tags. Selecting certain tags displays all windows with these tags. | |||
.P | |||
Each screen contains a small status bar which displays all available tags, the | |||
layout, the title of the focused window, and the text read from the root window | |||
name property, if the screen is focused. A floating window is indicated with an | |||
empty square and a maximised floating window is indicated with a filled square | |||
before the windows title. The selected tags are indicated with a different | |||
color. The tags of the focused window are indicated with a filled square in the | |||
top left corner. The tags which are applied to one or more windows are | |||
indicated with an empty square in the top left corner. | |||
Each screen contains two small status bars. | |||
.P | |||
One bar displays all available tags, the layout, the title of the focused | |||
window, and the text read from the root window name property, if the screen is | |||
focused. A floating window is indicated with an empty square and a maximised | |||
floating window is indicated with a filled square before the windows title. The | |||
selected tags are indicated with a different color. The tags of the focused | |||
window are indicated with a filled square in the top left corner. The tags | |||
which are applied to one or more windows are indicated with an empty square in | |||
the top left corner. | |||
.P | |||
Another bar contains a tab for each window of the current view and allows | |||
navigation between windows, especially in the monocle mode. The different | |||
display modes of this bar are described under the Mod1\-w Keybord command | |||
section. When a single tag is selected, that tag is indicated in the left corner | |||
of the tab bar. | |||
.P | |||
dwm draws a small border around windows to indicate the focus state. | |||
.SH OPTIONS | |||
@@ -44,7 +52,8 @@ command. | |||
.TP | |||
.B Button1 | |||
click on a tag label to display all windows with that tag, click on the layout | |||
label toggles between tiled and floating layout. | |||
label toggles between tiled and floating layout, click on a window name in the | |||
tab bar brings focus to that window. | |||
.TP | |||
.B Button3 | |||
click on a tag label adds/removes all windows with that tag to/from the view. | |||
@@ -110,6 +119,12 @@ Increase master area size. | |||
.B Mod1\-h | |||
Decrease master area size. | |||
.TP | |||
.B Mod1\-w | |||
Cycle over the tab bar display modes: never displayed, always displayed, | |||
displayed only in monocle mode when the view contains than one window (auto | |||
mode). Some display modes can be disabled in the configuration, config.h. In | |||
the default configuration only "never" and "auto" display modes are enabled. | |||
.TP | |||
.B Mod1\-Return | |||
Zooms/cycles focused window to/from master area (tiled layouts only). | |||
.TP | |||
@@ -0,0 +1,176 @@ | |||
.TH DWM 1 dwm\-VERSION | |||
.SH NAME | |||
dwm \- dynamic window manager | |||
.SH SYNOPSIS | |||
.B dwm | |||
.RB [ \-v ] | |||
.SH DESCRIPTION | |||
dwm is a dynamic window manager for X. It manages windows in tiled, monocle | |||
and floating layouts. Either layout can be applied dynamically, optimising the | |||
environment for the application in use and the task performed. | |||
.P | |||
In tiled layouts windows are managed in a master and stacking area. The master | |||
area on the left contains one window by default, and the stacking area on the | |||
right contains all other windows. The number of master area windows can be | |||
adjusted from zero to an arbitrary number. In monocle layout all windows are | |||
maximised to the screen size. In floating layout windows can be resized and | |||
moved freely. Dialog windows are always managed floating, regardless of the | |||
layout applied. | |||
.P | |||
Windows are grouped by tags. Each window can be tagged with one or multiple | |||
tags. Selecting certain tags displays all windows with these tags. | |||
.P | |||
Each screen contains a small status bar which displays all available tags, the | |||
layout, the title of the focused window, and the text read from the root window | |||
name property, if the screen is focused. A floating window is indicated with an | |||
empty square and a maximised floating window is indicated with a filled square | |||
before the windows title. The selected tags are indicated with a different | |||
color. The tags of the focused window are indicated with a filled square in the | |||
top left corner. The tags which are applied to one or more windows are | |||
indicated with an empty square in the top left corner. | |||
.P | |||
dwm draws a small border around windows to indicate the focus state. | |||
.SH OPTIONS | |||
.TP | |||
.B \-v | |||
prints version information to standard output, then exits. | |||
.SH USAGE | |||
.SS Status bar | |||
.TP | |||
.B X root window name | |||
is read and displayed in the status text area. It can be set with the | |||
.BR xsetroot (1) | |||
command. | |||
.TP | |||
.B Button1 | |||
click on a tag label to display all windows with that tag, click on the layout | |||
label toggles between tiled and floating layout. | |||
.TP | |||
.B Button3 | |||
click on a tag label adds/removes all windows with that tag to/from the view. | |||
.TP | |||
.B Mod1\-Button1 | |||
click on a tag label applies that tag to the focused window. | |||
.TP | |||
.B Mod1\-Button3 | |||
click on a tag label adds/removes that tag to/from the focused window. | |||
.SS Keyboard commands | |||
.TP | |||
.B Mod1\-Shift\-Return | |||
Start | |||
.BR st(1). | |||
.TP | |||
.B Mod1\-p | |||
Spawn | |||
.BR dmenu(1) | |||
for launching other programs. | |||
.TP | |||
.B Mod1\-, | |||
Focus previous screen, if any. | |||
.TP | |||
.B Mod1\-. | |||
Focus next screen, if any. | |||
.TP | |||
.B Mod1\-Shift\-, | |||
Send focused window to previous screen, if any. | |||
.TP | |||
.B Mod1\-Shift\-. | |||
Send focused window to next screen, if any. | |||
.TP | |||
.B Mod1\-b | |||
Toggles bar on and off. | |||
.TP | |||
.B Mod1\-t | |||
Sets tiled layout. | |||
.TP | |||
.B Mod1\-f | |||
Sets floating layout. | |||
.TP | |||
.B Mod1\-m | |||
Sets monocle layout. | |||
.TP | |||
.B Mod1\-space | |||
Toggles between current and previous layout. | |||
.TP | |||
.B Mod1\-j | |||
Focus next window. | |||
.TP | |||
.B Mod1\-k | |||
Focus previous window. | |||
.TP | |||
.B Mod1\-i | |||
Increase number of windows in master area. | |||
.TP | |||
.B Mod1\-d | |||
Decrease number of windows in master area. | |||
.TP | |||
.B Mod1\-l | |||
Increase master area size. | |||
.TP | |||
.B Mod1\-h | |||
Decrease master area size. | |||
.TP | |||
.B Mod1\-Return | |||
Zooms/cycles focused window to/from master area (tiled layouts only). | |||
.TP | |||
.B Mod1\-Shift\-c | |||
Close focused window. | |||
.TP | |||
.B Mod1\-Shift\-space | |||
Toggle focused window between tiled and floating state. | |||
.TP | |||
.B Mod1\-Tab | |||
Toggles to the previously selected tags. | |||
.TP | |||
.B Mod1\-Shift\-[1..n] | |||
Apply nth tag to focused window. | |||
.TP | |||
.B Mod1\-Shift\-0 | |||
Apply all tags to focused window. | |||
.TP | |||
.B Mod1\-Control\-Shift\-[1..n] | |||
Add/remove nth tag to/from focused window. | |||
.TP | |||
.B Mod1\-[1..n] | |||
View all windows with nth tag. | |||
.TP | |||
.B Mod1\-0 | |||
View all windows with any tag. | |||
.TP | |||
.B Mod1\-Control\-[1..n] | |||
Add/remove all windows with nth tag to/from the view. | |||
.TP | |||
.B Mod1\-Shift\-q | |||
Quit dwm. | |||
.SS Mouse commands | |||
.TP | |||
.B Mod1\-Button1 | |||
Move focused window while dragging. Tiled windows will be toggled to the floating state. | |||
.TP | |||
.B Mod1\-Button2 | |||
Toggles focused window between floating and tiled state. | |||
.TP | |||
.B Mod1\-Button3 | |||
Resize focused window while dragging. Tiled windows will be toggled to the floating state. | |||
.SH CUSTOMIZATION | |||
dwm is customized by creating a custom config.h and (re)compiling the source | |||
code. This keeps it fast, secure and simple. | |||
.SH SEE ALSO | |||
.BR dmenu (1), | |||
.BR st (1) | |||
.SH ISSUES | |||
Java applications which use the XToolkit/XAWT backend may draw grey windows | |||
only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early | |||
JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds | |||
are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the | |||
environment variable | |||
.BR AWT_TOOLKIT=MToolkit | |||
(to use the older Motif backend instead) or running | |||
.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D | |||
or | |||
.B wmname LG3D | |||
(to pretend that a non-reparenting window manager is running that the | |||
XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable | |||
.BR _JAVA_AWT_WM_NONREPARENTING=1 . | |||
.SH BUGS | |||
Send all bug reports with a patch to hackers@suckless.org. |
@@ -70,7 +70,7 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, | |||
NetWMFullscreen, NetActiveWindow, NetWMWindowType, | |||
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ | |||
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ | |||
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, | |||
enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, | |||
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ | |||
typedef union { | |||
@@ -119,12 +119,16 @@ typedef struct { | |||
void (*arrange)(Monitor *); | |||
} Layout; | |||
#define MAXTABS 50 | |||
typedef struct Pertag Pertag; | |||
struct Monitor { | |||
char ltsymbol[16]; | |||
float mfact; | |||
int nmaster; | |||
int num; | |||
int by; /* bar geometry */ | |||
int ty; /* tab bar geometry */ | |||
int mx, my, mw, mh; /* screen size */ | |||
int wx, wy, ww, wh; /* window area */ | |||
int gappx; /* gaps between windows */ | |||
@@ -132,13 +136,19 @@ struct Monitor { | |||
unsigned int sellt; | |||
unsigned int tagset[2]; | |||
int showbar; | |||
int showtab; | |||
int topbar; | |||
int toptab; | |||
Client *clients; | |||
Client *sel; | |||
Client *stack; | |||
Monitor *next; | |||
Window barwin; | |||
Window tabwin; | |||
int ntabs; | |||
int tab_widths[MAXTABS] ; | |||
const Layout *lt[2]; | |||
Pertag *pertag; | |||
}; | |||
typedef struct { | |||
@@ -175,12 +185,15 @@ static void detachstack(Client *c); | |||
static Monitor *dirtomon(int dir); | |||
static void drawbar(Monitor *m); | |||
static void drawbars(void); | |||
static void drawtab(Monitor *m); | |||
static void drawtabs(void); | |||
static void enternotify(XEvent *e); | |||
static void expose(XEvent *e); | |||
static void focus(Client *c); | |||
static void focusin(XEvent *e); | |||
static void focusmon(const Arg *arg); | |||
static void focusstack(const Arg *arg); | |||
static void focuswin(const Arg* arg); | |||
static int getrootptr(int *x, int *y); | |||
static long getstate(Window w); | |||
static int gettextprop(Window w, Atom atom, char *text, unsigned int size); | |||
@@ -219,6 +232,7 @@ static void seturgent(Client *c, int urg); | |||
static void showhide(Client *c); | |||
static void sigchld(int unused); | |||
static void spawn(const Arg *arg); | |||
static void tabmode(const Arg *arg); | |||
static void tag(const Arg *arg); | |||
static void tagmon(const Arg *arg); | |||
static void tile(Monitor *); | |||
@@ -263,6 +277,7 @@ static char stext[256]; | |||
static int screen; | |||
static int sw, sh; /* X display screen geometry width, height */ | |||
static int bh, blw = 0; /* bar geometry */ | |||
static int th = 0; /* tab bar geometry */ | |||
static int lrpad; /* sum of left and right padding for text */ | |||
static int (*xerrorxlib)(Display *, XErrorEvent *); | |||
static unsigned int numlockmask = 0; | |||
@@ -296,6 +311,16 @@ static xcb_connection_t *xcon; | |||
/* configuration, allows nested code to access above variables */ | |||
#include "config.h" | |||
struct Pertag { | |||
unsigned int curtag, prevtag; /* current and previous tag */ | |||
int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ | |||
float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ | |||
unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ | |||
const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ | |||
Bool showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ | |||
Client *prevzooms[LENGTH(tags) + 1]; /* store zoom information */ | |||
}; | |||
/* compile-time check if all tags fit into an unsigned int bit array. */ | |||
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; | |||
@@ -451,6 +476,8 @@ arrange(Monitor *m) | |||
void | |||
arrangemon(Monitor *m) | |||
{ | |||
updatebarpos(m); | |||
XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); | |||
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); | |||
if (m->lt[m->sellt]->arrange) | |||
m->lt[m->sellt]->arrange(m); | |||
@@ -547,16 +574,35 @@ buttonpress(XEvent *e) | |||
click = ClkStatusText; | |||
else | |||
click = ClkWinTitle; | |||
} else if ((c = wintoclient(ev->window))) { | |||
} | |||
if(ev->window == selmon->tabwin) { | |||
i = 0; x = 0; | |||
for(c = selmon->clients; c; c = c->next){ | |||
if(!ISVISIBLE(c)) continue; | |||
x += selmon->tab_widths[i]; | |||
if (ev->x > x) | |||
++i; | |||
else | |||
break; | |||
if(i >= m->ntabs) break; | |||
} | |||
if(c) { | |||
click = ClkTabBar; | |||
arg.ui = i; | |||
} | |||
} | |||
else if((c = wintoclient(ev->window))) { | |||
focus(c); | |||
restack(selmon); | |||
XAllowEvents(dpy, ReplayPointer, CurrentTime); | |||
click = ClkClientWin; | |||
} | |||
for (i = 0; i < LENGTH(buttons); i++) | |||
if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | |||
&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) | |||
buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); | |||
for(i = 0; i < LENGTH(buttons); i++) | |||
if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | |||
&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ | |||
buttons[i].func(((click == ClkTagBar || click == ClkTabBar) | |||
&& buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); | |||
} | |||
} | |||
void | |||
@@ -581,8 +627,8 @@ cleanup(void) | |||
view(&a); | |||
selmon->lt[selmon->sellt] = &foo; | |||
for (m = mons; m; m = m->next) | |||
while (m->stack) | |||
unmanage(m->stack, 0); | |||
while(m->stack) | |||
unmanage(m->stack, False); | |||
XUngrabKey(dpy, AnyKey, AnyModifier, root); | |||
while (mons) | |||
cleanupmon(mons); | |||
@@ -610,6 +656,8 @@ cleanupmon(Monitor *mon) | |||
} | |||
XUnmapWindow(dpy, mon->barwin); | |||
XDestroyWindow(dpy, mon->barwin); | |||
XUnmapWindow(dpy, mon->tabwin); | |||
XDestroyWindow(dpy, mon->tabwin); | |||
free(mon); | |||
} | |||
@@ -618,6 +666,7 @@ clientmessage(XEvent *e) | |||
{ | |||
XClientMessageEvent *cme = &e->xclient; | |||
Client *c = wintoclient(cme->window); | |||
int i; | |||
if (!c) | |||
return; | |||
@@ -655,11 +704,10 @@ void | |||
configurenotify(XEvent *e) | |||
{ | |||
Monitor *m; | |||
Client *c; | |||
XConfigureEvent *ev = &e->xconfigure; | |||
int dirty; | |||
/* TODO: updategeom handling sucks, needs to be simplified */ | |||
// TODO: updategeom handling sucks, needs to be simplified | |||
if (ev->window == root) { | |||
dirty = (sw != ev->width || sh != ev->height); | |||
sw = ev->width; | |||
@@ -667,10 +715,9 @@ configurenotify(XEvent *e) | |||
if (updategeom() || dirty) { | |||
drw_resize(drw, sw, bh); | |||
updatebars(); | |||
for (m = mons; m; m = m->next) { | |||
for (c = m->clients; c; c = c->next) | |||
if (c->isfullscreen) | |||
resizeclient(c, m->mx, m->my, m->mw, m->mh); | |||
//refreshing display of status bar. The tab bar is handled by the arrange() | |||
//method, which is called below | |||
for (m = mons; m; m = m->next){ | |||
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); | |||
} | |||
focus(NULL); | |||
@@ -735,17 +782,42 @@ Monitor * | |||
createmon(void) | |||
{ | |||
Monitor *m; | |||
int i ; | |||
m = ecalloc(1, sizeof(Monitor)); | |||
m->tagset[0] = m->tagset[1] = 1; | |||
m->mfact = mfact; | |||
m->nmaster = nmaster; | |||
m->showbar = showbar; | |||
m->topbar = topbar; | |||
m->showtab = showtab; | |||
m->topbar = topbar; | |||
m->gappx = gappx; | |||
m->lt[0] = &layouts[0]; | |||
m->toptab = toptab; | |||
m->ntabs = 0; | |||
m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)]; | |||
m->lt[1] = &layouts[1 % LENGTH(layouts)]; | |||
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); | |||
if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) | |||
die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); | |||
m->pertag->curtag = m->pertag->prevtag = 1; | |||
for(i=0; i <= LENGTH(tags); i++) { | |||
/* init nmaster */ | |||
m->pertag->nmasters[i] = m->nmaster; | |||
/* init mfacts */ | |||
m->pertag->mfacts[i] = m->mfact; | |||
/* init layouts */ | |||
m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)]; | |||
m->pertag->ltidxs[i][1] = m->lt[1]; | |||
m->pertag->sellts[i] = m->sellt; | |||
/* init showbar */ | |||
m->pertag->showbars[i] = m->showbar; | |||
/* swap focus and zoomswap*/ | |||
m->pertag->prevzooms[i] = NULL; | |||
} | |||
return m; | |||
} | |||
@@ -862,6 +934,104 @@ drawbars(void) | |||
drawbar(m); | |||
} | |||
void | |||
drawtabs(void) { | |||
Monitor *m; | |||
for(m = mons; m; m = m->next) | |||
drawtab(m); | |||
} | |||
static int | |||
cmpint(const void *p1, const void *p2) { | |||
/* The actual arguments to this function are "pointers to | |||
pointers to char", but strcmp(3) arguments are "pointers | |||
to char", hence the following cast plus dereference */ | |||
return *((int*) p1) > * (int*) p2; | |||
} | |||
void | |||
drawtab(Monitor *m) { | |||
Client *c; | |||
int i; | |||
int itag = -1; | |||
char view_info[50]; | |||
int view_info_w = 0; | |||
int sorted_label_widths[MAXTABS]; | |||
int tot_width; | |||
int maxsize = bh; | |||
int x = 0; | |||
int w = 0; | |||
//view_info: indicate the tag which is displayed in the view | |||
for(i = 0; i < LENGTH(tags); ++i){ | |||
if((selmon->tagset[selmon->seltags] >> i) & 1) { | |||
if(itag >=0){ //more than one tag selected | |||
itag = -1; | |||
break; | |||
} | |||
itag = i; | |||
} | |||
} | |||
if(0 <= itag && itag < LENGTH(tags)){ | |||
snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); | |||
} else { | |||
strncpy(view_info, "[...]", sizeof view_info); | |||
} | |||
view_info[sizeof(view_info) - 1 ] = 0; | |||
view_info_w = TEXTW(view_info); | |||
tot_width = view_info_w; | |||
/* Calculates number of labels and their width */ | |||
m->ntabs = 0; | |||
for(c = m->clients; c; c = c->next){ | |||
if(!ISVISIBLE(c)) continue; | |||
m->tab_widths[m->ntabs] = TEXTW(c->name); | |||
tot_width += m->tab_widths[m->ntabs]; | |||
++m->ntabs; | |||
if(m->ntabs >= MAXTABS) break; | |||
} | |||
if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated | |||
memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); | |||
qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); | |||
tot_width = view_info_w; | |||
for(i = 0; i < m->ntabs; ++i){ | |||
if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) | |||
break; | |||
tot_width += sorted_label_widths[i]; | |||
} | |||
maxsize = (m->ww - tot_width) / (m->ntabs - i); | |||
} else{ | |||
maxsize = m->ww; | |||
} | |||
i = 0; | |||
for(c = m->clients; c; c = c->next){ | |||
if(!ISVISIBLE(c)) continue; | |||
if(i >= m->ntabs) break; | |||
if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; | |||
w = m->tab_widths[i]; | |||
drw_setscheme(drw, (c == m->sel) ? scheme[SchemeSel] : scheme[SchemeNorm]); | |||
drw_text(drw, x, 0, w, th, lrpad / 2, c->name, 0); | |||
x += w; | |||
++i; | |||
} | |||
drw_setscheme(drw, scheme[SchemeNorm]); | |||
/* cleans interspace between window names and current viewed tag label */ | |||
w = m->ww - view_info_w - x; | |||
drw_text(drw, x, 0, w, th, lrpad / 2, "", 0); | |||
/* view info */ | |||
x += w; | |||
w = view_info_w; | |||
drw_text(drw, x, 0, w, th, lrpad / 2, view_info, 0); | |||
drw_map(drw, m->tabwin, 0, 0, m->ww, th); | |||
} | |||
void | |||
enternotify(XEvent *e) | |||
{ | |||
@@ -887,8 +1057,10 @@ expose(XEvent *e) | |||
Monitor *m; | |||
XExposeEvent *ev = &e->xexpose; | |||
if (ev->count == 0 && (m = wintomon(ev->window))) | |||
if (ev->count == 0 && (m = wintomon(ev->window))){ | |||
drawbar(m); | |||
drawtab(m); | |||
} | |||
} | |||
void | |||
@@ -914,6 +1086,7 @@ focus(Client *c) | |||
} | |||
selmon->sel = c; | |||
drawbars(); | |||
drawtabs(); | |||
} | |||
/* there are some broken focus acquiring clients needing extra handling */ | |||
@@ -966,6 +1139,19 @@ focusstack(const Arg *arg) | |||
} | |||
} | |||
void | |||
focuswin(const Arg* arg){ | |||
int iwin = arg->i; | |||
Client* c = NULL; | |||
for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ | |||
if(ISVISIBLE(c)) --iwin; | |||
}; | |||
if(c) { | |||
focus(c); | |||
restack(selmon); | |||
} | |||
} | |||
Atom | |||
getatomprop(Client *c, Atom prop) | |||
{ | |||
@@ -1077,7 +1263,7 @@ grabkeys(void) | |||
void | |||
incnmaster(const Arg *arg) | |||
{ | |||
selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); | |||
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); | |||
arrange(selmon); | |||
} | |||
@@ -1244,7 +1430,7 @@ motionnotify(XEvent *e) | |||
if (ev->window != root) | |||
return; | |||
if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { | |||
unfocus(selmon->sel, 1); | |||
unfocus(selmon->sel, True); | |||
selmon = m; | |||
focus(NULL); | |||
} | |||
@@ -1352,12 +1538,14 @@ propertynotify(XEvent *e) | |||
case XA_WM_HINTS: | |||
updatewmhints(c); | |||
drawbars(); | |||
drawtabs(); | |||
break; | |||
} | |||
if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { | |||
updatetitle(c); | |||
if (c == c->mon->sel) | |||
drawbar(c->mon); | |||
drawtab(c->mon); | |||
} | |||
if (ev->atom == netatom[NetWMWindowType]) | |||
updatewindowtype(c); | |||
@@ -1481,6 +1669,7 @@ restack(Monitor *m) | |||
XWindowChanges wc; | |||
drawbar(m); | |||
drawtab(m); | |||
if (!m->sel) | |||
return; | |||
if (m->sel->isfloating || !m->lt[m->sellt]->arrange) | |||
@@ -1650,10 +1839,13 @@ setgaps(const Arg *arg) | |||
void | |||
setlayout(const Arg *arg) | |||
{ | |||
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) | |||
selmon->sellt ^= 1; | |||
if (arg && arg->v) | |||
selmon->lt[selmon->sellt] = (Layout *)arg->v; | |||
if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { | |||
selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; | |||
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; | |||
} | |||
if(arg && arg->v) | |||
selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; | |||
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; | |||
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); | |||
if (selmon->sel) | |||
arrange(selmon); | |||
@@ -1672,7 +1864,7 @@ setmfact(const Arg *arg) | |||
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; | |||
if (f < 0.1 || f > 0.9) | |||
return; | |||
selmon->mfact = f; | |||
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; | |||
arrange(selmon); | |||
} | |||
@@ -1696,6 +1888,7 @@ setup(void) | |||
die("no fonts could be loaded."); | |||
lrpad = drw->fonts->h; | |||
bh = drw->fonts->h + 2; | |||
th = bh; | |||
updategeom(); | |||
/* init atoms */ | |||
utf8string = XInternAtom(dpy, "UTF8_STRING", False); | |||
@@ -1805,10 +1998,10 @@ sigchld(int unused) | |||
void | |||
spawn(const Arg *arg) | |||
{ | |||
if (arg->v == dmenucmd) | |||
if(arg->v == dmenucmd) | |||
dmenumon[0] = '0' + selmon->num; | |||
if (fork() == 0) { | |||
if (dpy) | |||
if(fork() == 0) { | |||
if(dpy) | |||
close(ConnectionNumber(dpy)); | |||
setsid(); | |||
execvp(((char **)arg->v)[0], (char **)arg->v); | |||
@@ -1865,18 +2058,29 @@ tile(Monitor *m) | |||
void | |||
togglebar(const Arg *arg) | |||
{ | |||
selmon->showbar = !selmon->showbar; | |||
selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; | |||
updatebarpos(selmon); | |||
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); | |||
arrange(selmon); | |||
} | |||
void | |||
tabmode(const Arg *arg) | |||
{ | |||
if(arg && arg->i >= 0) | |||
selmon->showtab = arg->ui % showtab_nmodes; | |||
else | |||
selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes; | |||
arrange(selmon); | |||
} | |||
void | |||
togglefloating(const Arg *arg) | |||
{ | |||
if (!selmon->sel) | |||
if(!selmon->sel) | |||
return; | |||
if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ | |||
if(selmon->sel->isfullscreen) /* no support for fullscreen windows */ | |||
return; | |||
selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; | |||
if (selmon->sel->isfloating) | |||
@@ -1930,9 +2134,29 @@ void | |||
toggleview(const Arg *arg) | |||
{ | |||
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); | |||
int i; | |||
if (newtagset) { | |||
if(newtagset == ~0) { | |||
selmon->pertag->prevtag = selmon->pertag->curtag; | |||
selmon->pertag->curtag = 0; | |||
} | |||
/* test if the user did not select the same tag */ | |||
if(!(newtagset & 1 << (selmon->pertag->curtag - 1))) { | |||
selmon->pertag->prevtag = selmon->pertag->curtag; | |||
for (i=0; !(newtagset & 1 << i); i++) ; | |||
selmon->pertag->curtag = i + 1; | |||
} | |||
selmon->tagset[selmon->seltags] = newtagset; | |||
/* apply settings for this view */ | |||
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; | |||
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; | |||
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; | |||
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; | |||
selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; | |||
if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) | |||
togglebar(NULL); | |||
focus(NULL); | |||
arrange(selmon); | |||
} | |||
@@ -2025,6 +2249,11 @@ updatebars(void) | |||
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | |||
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); | |||
XMapRaised(dpy, m->barwin); | |||
m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), | |||
CopyFromParent, DefaultVisual(dpy, screen), | |||
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | |||
XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); | |||
XMapRaised(dpy, m->tabwin); | |||
XSetClassHint(dpy, m->barwin, &ch); | |||
} | |||
} | |||
@@ -2032,14 +2261,33 @@ updatebars(void) | |||
void | |||
updatebarpos(Monitor *m) | |||
{ | |||
m->wy = m->my; | |||
Client *c; | |||
int nvis = 0; | |||
m->wy = m->my; | |||
m->wh = m->mh; | |||
if (m->showbar) { | |||
m->wh -= bh; | |||
m->by = m->topbar ? m->wy : m->wy + m->wh; | |||
m->wy = m->topbar ? m->wy + bh : m->wy; | |||
} else | |||
m->by = -bh; | |||
if ( m->topbar ) | |||
m->wy += bh; | |||
} else { | |||
m->by = -bh; | |||
} | |||
for(c = m->clients; c; c = c->next){ | |||
if(ISVISIBLE(c)) ++nvis; | |||
} | |||
if(m->showtab == showtab_always | |||
|| ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){ | |||
m->wh -= th; | |||
m->ty = m->toptab ? m->wy : m->wy + m->wh; | |||
if ( m->toptab ) | |||
m->wy += th; | |||
} else { | |||
m->ty = -th; | |||
} | |||
} | |||
void | |||
@@ -2217,10 +2465,10 @@ updatewindowtype(Client *c) | |||
Atom wtype = getatomprop(c, netatom[NetWMWindowType]); | |||
if (state == netatom[NetWMFullscreen]) | |||
setfullscreen(c, 1); | |||
setfullscreen(c, True); | |||
if (wtype == netatom[NetWMWindowTypeDialog]) { | |||
c->iscentered = 1; | |||
c->isfloating = 1; | |||
c->isfloating = True; | |||
} | |||
} | |||
@@ -2246,11 +2494,33 @@ updatewmhints(Client *c) | |||
void | |||
view(const Arg *arg) | |||
{ | |||
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | |||
int i; | |||
unsigned int tmptag; | |||
if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | |||
return; | |||
selmon->seltags ^= 1; /* toggle sel tagset */ | |||
if (arg->ui & TAGMASK) | |||
if(arg->ui & TAGMASK) { | |||
selmon->pertag->prevtag = selmon->pertag->curtag; | |||
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; | |||
if(arg->ui == ~0) | |||
selmon->pertag->curtag = 0; | |||
else { | |||
for (i=0; !(arg->ui & 1 << i); i++) ; | |||
selmon->pertag->curtag = i + 1; | |||
} | |||
} else { | |||
tmptag = selmon->pertag->prevtag; | |||
selmon->pertag->prevtag = selmon->pertag->curtag; | |||
selmon->pertag->curtag = tmptag; | |||
} | |||
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; | |||
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; | |||
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; | |||
selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; | |||
selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; | |||
if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) | |||
togglebar(NULL); | |||
focus(NULL); | |||
arrange(selmon); | |||
} | |||
@@ -2408,7 +2678,7 @@ wintomon(Window w) | |||
if (w == root && getrootptr(&x, &y)) | |||
return recttomon(x, y, 1, 1); | |||
for (m = mons; m; m = m->next) | |||
if (w == m->barwin) | |||
if(w == m->barwin || w == m->tabwin) | |||
return m; | |||
if ((c = wintoclient(w))) | |||
return c->mon; | |||
@@ -0,0 +1,233 @@ | |||
--- dwm.c | |||
+++ dwm.c | |||
@@ -112,25 +112,35 @@ typedef struct { | |||
void (*arrange)(Monitor *); | |||
} Layout; | |||
+#define MAXTABS 50 | |||
+ | |||
+typedef struct Pertag Pertag; | |||
struct Monitor { | |||
char ltsymbol[16]; | |||
float mfact; | |||
int nmaster; | |||
int num; | |||
int by; /* bar geometry */ | |||
+ int ty; /* tab bar geometry */ | |||
int mx, my, mw, mh; /* screen size */ | |||
int wx, wy, ww, wh; /* window area */ | |||
unsigned int seltags; | |||
unsigned int sellt; | |||
unsigned int tagset[2]; | |||
int showbar; | |||
+ int showtab; | |||
int topbar; | |||
+ int toptab; | |||
Client *clients; | |||
Client *sel; | |||
Client *stack; | |||
Monitor *next; | |||
Window barwin; | |||
+ Window tabwin; | |||
+ int ntabs; | |||
+ int tab_widths[MAXTABS]; | |||
const Layout *lt[2]; | |||
+ Pertag *pertag; | |||
}; | |||
typedef struct { | |||
@@ -461,14 +488,33 @@ buttonpress(XEvent *e) | |||
click = ClkStatusText; | |||
else | |||
click = ClkWinTitle; | |||
- } else if ((c = wintoclient(ev->window))) { | |||
+ } | |||
+ if(ev->window == selmon->tabwin) { | |||
+ i = 0; x = 0; | |||
+ for(c = selmon->clients; c; c = c->next){ | |||
+ if(!ISVISIBLE(c)) continue; | |||
+ x += selmon->tab_widths[i]; | |||
+ if (ev->x > x) | |||
+ ++i; | |||
+ else | |||
+ break; | |||
+ if(i >= m->ntabs) break; | |||
+ } | |||
+ if(c) { | |||
+ click = ClkTabBar; | |||
+ arg.ui = i; | |||
+ } | |||
+ } | |||
+ else if((c = wintoclient(ev->window))) { | |||
focus(c); | |||
click = ClkClientWin; | |||
} | |||
- for (i = 0; i < LENGTH(buttons); i++) | |||
- if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | |||
- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) | |||
- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); | |||
+ for(i = 0; i < LENGTH(buttons); i++) | |||
+ if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button | |||
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){ | |||
+ buttons[i].func(((click == ClkTagBar || click == ClkTabBar) | |||
+ && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg); | |||
+ } | |||
} | |||
void | |||
@@ -556,6 +605,8 @@ clientmessage(XEvent *e) | |||
if (!ISVISIBLE(c)) { | |||
c->mon->seltags ^= 1; | |||
c->mon->tagset[c->mon->seltags] = c->tags; | |||
+ for(i=0; !(c->tags & 1 << i); i++); | |||
+ view(&(Arg){.ui = 1 << i}); | |||
} | |||
pop(c); | |||
} | |||
@@ -662,16 +711,41 @@ Monitor * | |||
createmon(void) | |||
{ | |||
Monitor *m; | |||
+ int i; | |||
m = ecalloc(1, sizeof(Monitor)); | |||
m->tagset[0] = m->tagset[1] = 1; | |||
m->mfact = mfact; | |||
m->nmaster = nmaster; | |||
m->showbar = showbar; | |||
+ m->showtab = showtab; | |||
m->topbar = topbar; | |||
- m->lt[0] = &layouts[0]; | |||
+ m->toptab = toptab; | |||
+ m->ntabs = 0; | |||
+ m->lt[0] = &layouts[def_layouts[1] % LENGTH(layouts)]; | |||
m->lt[1] = &layouts[1 % LENGTH(layouts)]; | |||
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); | |||
+ if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) | |||
+ die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); | |||
+ m->pertag->curtag = m->pertag->prevtag = 1; | |||
+ for(i=0; i <= LENGTH(tags); i++) { | |||
+ /* init nmaster */ | |||
+ m->pertag->nmasters[i] = m->nmaster; | |||
+ | |||
+ /* init mfacts */ | |||
+ m->pertag->mfacts[i] = m->mfact; | |||
+ | |||
+ /* init layouts */ | |||
+ m->pertag->ltidxs[i][0] = &layouts[def_layouts[i % LENGTH(def_layouts)] % LENGTH(layouts)]; | |||
+ m->pertag->ltidxs[i][1] = m->lt[1]; | |||
+ m->pertag->sellts[i] = m->sellt; | |||
+ | |||
+ /* init showbar */ | |||
+ m->pertag->showbars[i] = m->showbar; | |||
+ | |||
+ /* swap focus and zoomswap*/ | |||
+ m->pertag->prevzooms[i] = NULL; | |||
+ } | |||
return m; | |||
} | |||
@@ -1294,11 +1482,13 @@ movemouse(const Arg *arg) | |||
return; | |||
if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ | |||
return; | |||
+ if(c->isfullscreen) /* no support moving fullscreen windows by mouse */ | |||
+ return; | |||
restack(selmon); | |||
ocx = c->x; | |||
ocy = c->y; | |||
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | |||
- None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) | |||
+ None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) | |||
return; | |||
if (!getrootptr(&x, &y)) | |||
return; | |||
@@ -1454,11 +1646,13 @@ resizemouse(const Arg *arg) | |||
return; | |||
if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ | |||
return; | |||
+ if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */ | |||
+ return; | |||
restack(selmon); | |||
ocx = c->x; | |||
ocy = c->y; | |||
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, | |||
- None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) | |||
+ None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) | |||
return; | |||
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); | |||
do { | |||
@@ -1615,11 +1810,11 @@ sendevent(Client *c, Atom proto) | |||
void | |||
setfocus(Client *c) | |||
{ | |||
- if (!c->neverfocus) { | |||
+ if(!c->neverfocus) { | |||
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | |||
XChangeProperty(dpy, root, netatom[NetActiveWindow], | |||
- XA_WINDOW, 32, PropModeReplace, | |||
- (unsigned char *) &(c->win), 1); | |||
+ XA_WINDOW, 32, PropModeReplace, | |||
+ (unsigned char *) &(c->win), 1); | |||
} | |||
sendevent(c, wmatom[WMTakeFocus]); | |||
} | |||
@@ -1978,20 +2208,44 @@ updatebars(void) | |||
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | |||
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); | |||
XMapRaised(dpy, m->barwin); | |||
+ m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), | |||
+ CopyFromParent, DefaultVisual(dpy, screen), | |||
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); | |||
+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); | |||
+ XMapRaised(dpy, m->tabwin); | |||
} | |||
} | |||
void | |||
updatebarpos(Monitor *m) | |||
{ | |||
+ Client *c; | |||
+ int nvis = 0; | |||
+ | |||
m->wy = m->my; | |||
m->wh = m->mh; | |||
if (m->showbar) { | |||
m->wh -= bh; | |||
m->by = m->topbar ? m->wy : m->wy + m->wh; | |||
- m->wy = m->topbar ? m->wy + bh : m->wy; | |||
- } else | |||
+ if ( m->topbar ) | |||
+ m->wy += bh; | |||
+ } else { | |||
m->by = -bh; | |||
+ } | |||
+ | |||
+ for(c = m->clients; c; c = c->next){ | |||
+ if(ISVISIBLE(c)) ++nvis; | |||
+ } | |||
+ | |||
+ if(m->showtab == showtab_always | |||
+ || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){ | |||
+ m->wh -= th; | |||
+ m->ty = m->toptab ? m->wy : m->wy + m->wh; | |||
+ if ( m->toptab ) | |||
+ m->wy += th; | |||
+ } else { | |||
+ m->ty = -th; | |||
+ } | |||
} | |||
void | |||
@@ -2173,9 +2427,9 @@ updatewindowtype(Client *c) | |||
Atom wtype = getatomprop(c, netatom[NetWMWindowType]); | |||
if (state == netatom[NetWMFullscreen]) | |||
- setfullscreen(c, 1); | |||
+ setfullscreen(c, True); | |||
if (wtype == netatom[NetWMWindowTypeDialog]) | |||
- c->isfloating = 1; | |||
+ c->isfloating = True; | |||
} | |||
void |