From fa237823f23b313a175ae0d0a3bf5d9c06d0c43f Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 30 Jun 2025 20:24:32 -0600 Subject: [PATCH] idk --- .xinitrc | 8 + config.def.h | 5 + config.def.h.orig | 116 ++ config.h | 132 ++ drw.o | Bin 0 -> 11408 bytes dwm | Bin 0 -> 79920 bytes dwm-systray-20230922-9f88553.diff | 735 ++++++++++ dwm.c | 411 +++++- dwm.c.orig | 2164 +++++++++++++++++++++++++++++ dwm.c.rej | 16 + dwm.o | Bin 0 -> 69648 bytes util.o | Bin 0 -> 2480 bytes 12 files changed, 3561 insertions(+), 26 deletions(-) create mode 100644 .xinitrc create mode 100644 config.def.h.orig create mode 100644 config.h create mode 100644 drw.o create mode 100755 dwm create mode 100644 dwm-systray-20230922-9f88553.diff create mode 100644 dwm.c.orig create mode 100644 dwm.c.rej create mode 100644 dwm.o create mode 100644 util.o diff --git a/.xinitrc b/.xinitrc new file mode 100644 index 0000000..3b97de1 --- /dev/null +++ b/.xinitrc @@ -0,0 +1,8 @@ +export XDG_DATA_DIRS="/var/lib/flatpak/exports/share:/home/wes/.local/share/flatpak/exports/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" +export XDG_SESSION_TYPE=x11 +feh --bg-scale Downloads/coffee.jpg & +picom -b -c --backend xrender & +. /home/wes/.bashrc & +dwmblocks & +blueman-applet & +redshift -O3500; xset r rat 300 50; exec dwm diff --git a/config.def.h b/config.def.h index 9efa774..fed4fb9 100644 --- a/config.def.h +++ b/config.def.h @@ -3,6 +3,11 @@ /* appearance */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int snap = 32; /* snap pixel */ +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ +static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ +static const int showsystray = 1; /* 0 means no systray */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ static const char *fonts[] = { "monospace:size=10" }; diff --git a/config.def.h.orig b/config.def.h.orig new file mode 100644 index 0000000..9efa774 --- /dev/null +++ b/config.def.h.orig @@ -0,0 +1,116 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +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 */ +static const char *fonts[] = { "monospace:size=10" }; +static const char dmenufont[] = "monospace:size=10"; +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[] = "#005577"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, +}; + +/* 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 int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod1Mask +#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 }; + +static const Key keys[] = { + /* modifier key function argument */ + { 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_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 } }, + 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 const 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} }, +}; + diff --git a/config.h b/config.h new file mode 100644 index 0000000..e0275cd --- /dev/null +++ b/config.h @@ -0,0 +1,132 @@ +/* See LICENSE file for copyright and license details. */ + +#include + +/* appearance */ +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 */ +static const char *fonts[] = { "monospace:size=18", "Noto Color Emoji:size=18" }; +static const char dmenufont[] = "monospace:size=22"; +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[] = "#a9759e"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, +}; + +/* 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 int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define PrintScreenDWM 0x0000ff61 +#define MODKEY Mod1Mask +#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[] = { "alacritty", NULL }; + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_d, spawn, {.v = dmenucmd } }, + { MODKEY, 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_p, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY, XK_q, 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_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 } }, + 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_x, quit, {0} }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD("amixer set Master 5%-; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_AudioMute, spawn, SHCMD("amixer set Master toggle; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("amixer set Master 5%+; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_MonBrightnessUp, spawn, SHCMD("brightnessctl set 10%+; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_MonBrightnessDown,spawn, SHCMD("brightnessctl set 10%-; kill -44 $(pidof dwmblocks)") }, + { 0, XF86XK_AudioMicMute, spawn, SHCMD("amixer set Capture toggle; kill -44 $(pidof dwmblocks)") }, + { 0, PrintScreenDWM, spawn, SHCMD("flameshot gui") }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const 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} }, +}; + +/* systray settings */ +static const unsigned int systraypinning = 0; +static const unsigned int systrayonleft = 0; +static const unsigned int systrayspacing = 0; +static const int systraypinningfailfirst = 1; +static const int showsystray = 1; diff --git a/drw.o b/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..c93f3e03f8c8ea078a7cf21f20a4f28e794c7468 GIT binary patch literal 11408 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 literal 0 HcmV?d00001 diff --git a/dwm b/dwm new file mode 100755 index 0000000000000000000000000000000000000000..1a48ec2e7a1bd157fe4cd80acf8f9ff22ca3077f GIT binary patch literal 79920 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⪙;vHT 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: pin systray to monitor X */ ++static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ ++static const unsigned int systrayspacing = 2; /* systray spacing */ ++static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ ++static const int showsystray = 1; /* 0 means no systray */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +diff --git a/dwm.c b/dwm.c +index f1d86b2..f9e7e4a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -57,12 +57,27 @@ + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + ++#define SYSTEM_TRAY_REQUEST_DOCK 0 ++/* XEMBED messages */ ++#define XEMBED_EMBEDDED_NOTIFY 0 ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_FOCUS_IN 4 ++#define XEMBED_MODALITY_ON 10 ++#define XEMBED_MAPPED (1 << 0) ++#define XEMBED_WINDOW_ACTIVATE 1 ++#define XEMBED_WINDOW_DEACTIVATE 2 ++#define VERSION_MAJOR 0 ++#define VERSION_MINOR 0 ++#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR ++ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ ++enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ + enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +@@ -141,6 +156,12 @@ typedef struct { + int monitor; + } Rule; + ++typedef struct Systray Systray; ++struct Systray { ++ Window win; ++ Client *icons; ++}; ++ + /* function declarations */ + static void applyrules(Client *c); + static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +@@ -172,6 +193,7 @@ static void focusstack(const Arg *arg); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static unsigned int getsystraywidth(); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -189,13 +211,16 @@ static void pop(Client *c); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); ++static void removesystrayicon(Client *i); + static void resize(Client *c, int x, int y, int w, int h, int interact); ++static void resizebarwin(Monitor *m); + static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); ++static void resizerequest(XEvent *e); + static void restack(Monitor *m); + static void run(void); + static void scan(void); +-static int sendevent(Client *c, Atom proto); ++static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); + static void sendmon(Client *c, Monitor *m); + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); +@@ -206,6 +231,7 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void spawn(const Arg *arg); ++static Monitor *systraytomon(Monitor *m); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); +@@ -223,18 +249,23 @@ static int updategeom(void); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); ++static void updatesystray(void); ++static void updatesystrayicongeom(Client *i, int w, int h); ++static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); + static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); ++static Client *wintosystrayicon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); + static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + + /* variables */ ++static Systray *systray = NULL; + static const char broken[] = "broken"; + static char stext[256]; + static int screen; +@@ -257,9 +288,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, ++ [ResizeRequest] = resizerequest, + [UnmapNotify] = unmapnotify + }; +-static Atom wmatom[WMLast], netatom[NetLast]; ++static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -441,7 +473,7 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) + click = ClkStatusText; + else + click = ClkWinTitle; +@@ -484,6 +516,13 @@ cleanup(void) + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); ++ ++ if (showsystray) { ++ XUnmapWindow(dpy, systray->win); ++ XDestroyWindow(dpy, systray->win); ++ free(systray); ++ } ++ + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) +@@ -515,9 +554,58 @@ cleanupmon(Monitor *mon) + void + clientmessage(XEvent *e) + { ++ XWindowAttributes wa; ++ XSetWindowAttributes swa; + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + ++ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { ++ /* add systray icons */ ++ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { ++ if (!(c = (Client *)calloc(1, sizeof(Client)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); ++ if (!(c->win = cme->data.l[2])) { ++ free(c); ++ return; ++ } ++ c->mon = selmon; ++ c->next = systray->icons; ++ systray->icons = c; ++ if (!XGetWindowAttributes(dpy, c->win, &wa)) { ++ /* use sane defaults */ ++ wa.width = bh; ++ wa.height = bh; ++ wa.border_width = 0; ++ } ++ c->x = c->oldx = c->y = c->oldy = 0; ++ c->w = c->oldw = wa.width; ++ c->h = c->oldh = wa.height; ++ c->oldbw = wa.border_width; ++ c->bw = 0; ++ c->isfloating = True; ++ /* reuse tags field as mapped status */ ++ c->tags = 1; ++ updatesizehints(c); ++ updatesystrayicongeom(c, wa.width, wa.height); ++ XAddToSaveSet(dpy, c->win); ++ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); ++ XReparentWindow(dpy, c->win, systray->win, 0, 0); ++ /* use parents background color */ ++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ /* FIXME not sure if I have to send these events, too */ ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); ++ XSync(dpy, False); ++ resizebarwin(selmon); ++ updatesystray(); ++ setclientstate(c, NormalState); ++ } ++ return; ++ } ++ + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { +@@ -570,7 +658,7 @@ configurenotify(XEvent *e) + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); +- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); ++ resizebarwin(m); + } + focus(NULL); + arrange(NULL); +@@ -655,6 +743,11 @@ destroynotify(XEvent *e) + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); ++ else if ((c = wintosystrayicon(ev->window))) { ++ removesystrayicon(c); ++ resizebarwin(selmon); ++ updatesystray(); ++ } + } + + void +@@ -698,7 +791,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, stw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; +@@ -707,13 +800,17 @@ drawbar(Monitor *m) + if (!m->showbar) + return; + ++ if(showsystray && m == systraytomon(m) && !systrayonleft) ++ stw = getsystraywidth(); ++ + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */ ++ drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); + } + ++ resizebarwin(m); + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) +@@ -734,7 +831,7 @@ drawbar(Monitor *m) + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + +- if ((w = m->ww - tw - x) > bh) { ++ if ((w = m->ww - tw - stw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); +@@ -745,7 +842,7 @@ drawbar(Monitor *m) + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } +- drw_map(drw, m->barwin, 0, 0, m->ww, bh); ++ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + } + + void +@@ -782,8 +879,11 @@ 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); ++ if (m == selmon) ++ updatesystray(); ++ } + } + + void +@@ -869,14 +969,32 @@ getatomprop(Client *c, Atom prop) + unsigned char *p = NULL; + Atom da, atom = None; + +- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, ++ /* FIXME getatomprop should return the number of items and a pointer to ++ * the stored data instead of this workaround */ ++ Atom req = XA_ATOM; ++ if (prop == xatom[XembedInfo]) ++ req = xatom[XembedInfo]; ++ ++ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; ++ if (da == xatom[XembedInfo] && dl == 2) ++ atom = ((Atom *)p)[1]; + XFree(p); + } + return atom; + } + ++unsigned int ++getsystraywidth() ++{ ++ unsigned int w = 0; ++ Client *i; ++ if(showsystray) ++ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; ++ return w ? w + systrayspacing : 1; ++} ++ + int + getrootptr(int *x, int *y) + { +@@ -1017,7 +1135,8 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; +- if (!sendevent(selmon->sel, wmatom[WMDelete])) { ++ ++ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); +@@ -1104,6 +1223,13 @@ maprequest(XEvent *e) + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) +@@ -1225,6 +1351,17 @@ propertynotify(XEvent *e) + Window trans; + XPropertyEvent *ev = &e->xproperty; + ++ if ((c = wintosystrayicon(ev->window))) { ++ if (ev->atom == XA_WM_NORMAL_HINTS) { ++ updatesizehints(c); ++ updatesystrayicongeom(c, c->w, c->h); ++ } ++ else ++ updatesystrayiconstate(c, ev); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++ + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) +@@ -1275,6 +1412,19 @@ recttomon(int x, int y, int w, int h) + return r; + } + ++void ++removesystrayicon(Client *i) ++{ ++ Client **ii; ++ ++ if (!showsystray || !i) ++ return; ++ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); ++ if (ii) ++ *ii = i->next; ++ free(i); ++} ++ + void + resize(Client *c, int x, int y, int w, int h, int interact) + { +@@ -1282,6 +1432,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) + resizeclient(c, x, y, w, h); + } + ++void ++resizebarwin(Monitor *m) { ++ unsigned int w = m->ww; ++ if (showsystray && m == systraytomon(m) && !systrayonleft) ++ w -= getsystraywidth(); ++ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); ++} ++ + void + resizeclient(Client *c, int x, int y, int w, int h) + { +@@ -1297,6 +1455,19 @@ resizeclient(Client *c, int x, int y, int w, int h) + XSync(dpy, False); + } + ++void ++resizerequest(XEvent *e) ++{ ++ XResizeRequestEvent *ev = &e->xresizerequest; ++ Client *i; ++ ++ if ((i = wintosystrayicon(ev->window))) { ++ updatesystrayicongeom(i, ev->width, ev->height); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++} ++ + void + resizemouse(const Arg *arg) + { +@@ -1443,26 +1614,37 @@ setclientstate(Client *c, long state) + } + + int +-sendevent(Client *c, Atom proto) ++sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) + { + int n; +- Atom *protocols; ++ Atom *protocols, mt; + int exists = 0; + XEvent ev; + +- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { +- while (!exists && n--) +- exists = protocols[n] == proto; +- XFree(protocols); ++ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { ++ mt = wmatom[WMProtocols]; ++ if (XGetWMProtocols(dpy, w, &protocols, &n)) { ++ while (!exists && n--) ++ exists = protocols[n] == proto; ++ XFree(protocols); ++ } ++ } ++ else { ++ exists = True; ++ mt = proto; + } ++ + if (exists) { + ev.type = ClientMessage; +- ev.xclient.window = c->win; +- ev.xclient.message_type = wmatom[WMProtocols]; ++ ev.xclient.window = w; ++ ev.xclient.message_type = mt; + ev.xclient.format = 32; +- ev.xclient.data.l[0] = proto; +- ev.xclient.data.l[1] = CurrentTime; +- XSendEvent(dpy, c->win, False, NoEventMask, &ev); ++ ev.xclient.data.l[0] = d0; ++ ev.xclient.data.l[1] = d1; ++ ev.xclient.data.l[2] = d2; ++ ev.xclient.data.l[3] = d3; ++ ev.xclient.data.l[4] = d4; ++ XSendEvent(dpy, w, False, mask, &ev); + } + return exists; + } +@@ -1476,7 +1658,7 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } +- sendevent(c, wmatom[WMTakeFocus]); ++ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + } + + void +@@ -1572,6 +1754,10 @@ setup(void) + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); ++ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); ++ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ++ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); ++ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); +@@ -1579,6 +1765,9 @@ setup(void) + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); ++ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); ++ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); ++ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); +@@ -1587,6 +1776,8 @@ setup(void) + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); ++ /* init system tray */ ++ updatesystray(); + /* init bars */ + updatebars(); + updatestatus(); +@@ -1717,7 +1908,18 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ resizebarwin(selmon); ++ if (showsystray) { ++ XWindowChanges wc; ++ if (!selmon->showbar) ++ wc.y = -bh; ++ else if (selmon->showbar) { ++ wc.y = 0; ++ if (!selmon->topbar) ++ wc.y = selmon->mh - bh; ++ } ++ XConfigureWindow(dpy, systray->win, CWY, &wc); ++ } + arrange(selmon); + } + +@@ -1813,11 +2015,18 @@ unmapnotify(XEvent *e) + else + unmanage(c, 0); + } ++ else if ((c = wintosystrayicon(ev->window))) { ++ /* KLUDGE! sometimes icons occasionally unmap their windows, but do ++ * _not_ destroy them. We map those windows back */ ++ XMapRaised(dpy, c->win); ++ updatesystray(); ++ } + } + + void + updatebars(void) + { ++ unsigned int w; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, +@@ -1828,10 +2037,15 @@ updatebars(void) + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; +- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), ++ w = m->ww; ++ if (showsystray && m == systraytomon(m)) ++ w -= getsystraywidth(); ++ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); ++ if (showsystray && m == systraytomon(m)) ++ XMapRaised(dpy, systray->win); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +@@ -2008,6 +2222,125 @@ updatestatus(void) + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); ++ updatesystray(); ++} ++ ++ ++void ++updatesystrayicongeom(Client *i, int w, int h) ++{ ++ if (i) { ++ i->h = bh; ++ if (w == h) ++ i->w = bh; ++ else if (h == bh) ++ i->w = w; ++ else ++ i->w = (int) ((float)bh * ((float)w / (float)h)); ++ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); ++ /* force icons into the systray dimensions if they don't want to */ ++ if (i->h > bh) { ++ if (i->w == i->h) ++ i->w = bh; ++ else ++ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); ++ i->h = bh; ++ } ++ } ++} ++ ++void ++updatesystrayiconstate(Client *i, XPropertyEvent *ev) ++{ ++ long flags; ++ int code = 0; ++ ++ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || ++ !(flags = getatomprop(i, xatom[XembedInfo]))) ++ return; ++ ++ if (flags & XEMBED_MAPPED && !i->tags) { ++ i->tags = 1; ++ code = XEMBED_WINDOW_ACTIVATE; ++ XMapRaised(dpy, i->win); ++ setclientstate(i, NormalState); ++ } ++ else if (!(flags & XEMBED_MAPPED) && i->tags) { ++ i->tags = 0; ++ code = XEMBED_WINDOW_DEACTIVATE; ++ XUnmapWindow(dpy, i->win); ++ setclientstate(i, WithdrawnState); ++ } ++ else ++ return; ++ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, ++ systray->win, XEMBED_EMBEDDED_VERSION); ++} ++ ++void ++updatesystray(void) ++{ ++ XSetWindowAttributes wa; ++ XWindowChanges wc; ++ Client *i; ++ Monitor *m = systraytomon(NULL); ++ unsigned int x = m->mx + m->mw; ++ unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; ++ unsigned int w = 1; ++ ++ if (!showsystray) ++ return; ++ if (systrayonleft) ++ x -= sw + lrpad / 2; ++ if (!systray) { ++ /* init systray */ ++ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) ++ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); ++ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); ++ wa.event_mask = ButtonPressMask | ExposureMask; ++ wa.override_redirect = True; ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XSelectInput(dpy, systray->win, SubstructureNotifyMask); ++ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, ++ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); ++ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); ++ XMapRaised(dpy, systray->win); ++ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); ++ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { ++ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); ++ XSync(dpy, False); ++ } ++ else { ++ fprintf(stderr, "dwm: unable to obtain system tray.\n"); ++ free(systray); ++ systray = NULL; ++ return; ++ } ++ } ++ for (w = 0, i = systray->icons; i; i = i->next) { ++ /* make sure the background color stays the same */ ++ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; ++ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); ++ XMapRaised(dpy, i->win); ++ w += systrayspacing; ++ i->x = w; ++ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); ++ w += i->w; ++ if (i->mon != m) ++ i->mon = m; ++ } ++ w = w ? w + systrayspacing : 1; ++ x -= w; ++ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); ++ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; ++ wc.stack_mode = Above; wc.sibling = m->barwin; ++ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); ++ XMapWindow(dpy, systray->win); ++ XMapSubwindows(dpy, systray->win); ++ /* redraw background */ ++ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); ++ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); ++ XSync(dpy, False); + } + + void +@@ -2075,6 +2408,16 @@ wintoclient(Window w) + return NULL; + } + ++Client * ++wintosystrayicon(Window w) { ++ Client *i = NULL; ++ ++ if (!showsystray || !w) ++ return i; ++ for (i = systray->icons; i && i->win != w; i = i->next) ; ++ return i; ++} ++ + Monitor * + wintomon(Window w) + { +@@ -2128,6 +2471,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++Monitor * ++systraytomon(Monitor *m) { ++ Monitor *t; ++ int i, n; ++ if(!systraypinning) { ++ if(!m) ++ return selmon; ++ return m == selmon ? m : NULL; ++ } ++ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; ++ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; ++ if(systraypinningfailfirst && n < systraypinning) ++ return mons; ++ return t; ++} ++ + void + zoom(const Arg *arg) + { diff --git a/dwm.c b/dwm.c index 1443802..fbca3c8 100644 --- a/dwm.c +++ b/dwm.c @@ -56,12 +56,27 @@ #define TAGMASK ((1 << LENGTH(tags)) - 1) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define SYSTEM_TRAY_REQUEST_DOCK 0 +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_MODALITY_ON 10 +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR + /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ @@ -140,6 +155,12 @@ typedef struct { int monitor; } Rule; +typedef struct Systray Systray; +struct Systray { + Window win; + Client *icons; +}; + /* function declarations */ static void applyrules(Client *c); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); @@ -171,6 +192,7 @@ static void focusstack(const Arg *arg); static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); static long getstate(Window w); +static unsigned int getsystraywidth(); static int gettextprop(Window w, Atom atom, char *text, unsigned int size); static void grabbuttons(Client *c, int focused); static void grabkeys(void); @@ -188,13 +210,16 @@ static void pop(Client *c); static void propertynotify(XEvent *e); static void quit(const Arg *arg); static Monitor *recttomon(int x, int y, int w, int h); +static void removesystrayicon(Client *i); static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizebarwin(Monitor *m); static void resizeclient(Client *c, int x, int y, int w, int h); static void resizemouse(const Arg *arg); +static void resizerequest(XEvent *e); static void restack(Monitor *m); static void run(void); static void scan(void); -static int sendevent(Client *c, Atom proto); +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); static void sendmon(Client *c, Monitor *m); static void setclientstate(Client *c, long state); static void setfocus(Client *c); @@ -205,6 +230,7 @@ static void setup(void); static void seturgent(Client *c, int urg); static void showhide(Client *c); static void spawn(const Arg *arg); +static Monitor *systraytomon(Monitor *m); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); @@ -222,18 +248,23 @@ static int updategeom(void); static void updatenumlockmask(void); static void updatesizehints(Client *c); static void updatestatus(void); +static void updatesystray(void); +static void updatesystrayicongeom(Client *i, int w, int h); +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); static void updatetitle(Client *c); static void updatewindowtype(Client *c); static void updatewmhints(Client *c); static void view(const Arg *arg); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); +static Client *wintosystrayicon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); static void zoom(const Arg *arg); /* variables */ +static Systray *systray = NULL; static const char broken[] = "broken"; static char stext[256]; static int screen; @@ -256,9 +287,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { [MapRequest] = maprequest, [MotionNotify] = motionnotify, [PropertyNotify] = propertynotify, + [ResizeRequest] = resizerequest, [UnmapNotify] = unmapnotify }; -static Atom wmatom[WMLast], netatom[NetLast]; +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; static int running = 1; static Cur *cursor[CurLast]; static Clr **scheme; @@ -440,7 +472,7 @@ buttonpress(XEvent *e) arg.ui = 1 << i; } else if (ev->x < x + TEXTW(selmon->ltsymbol)) click = ClkLtSymbol; - else if (ev->x > selmon->ww - (int)TEXTW(stext)) + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) click = ClkStatusText; else click = ClkWinTitle; @@ -483,6 +515,13 @@ cleanup(void) XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); + + if (showsystray) { + XUnmapWindow(dpy, systray->win); + XDestroyWindow(dpy, systray->win); + free(systray); + } + for (i = 0; i < CurLast; i++) drw_cur_free(drw, cursor[i]); for (i = 0; i < LENGTH(colors); i++) @@ -514,9 +553,58 @@ cleanupmon(Monitor *mon) void clientmessage(XEvent *e) { + XWindowAttributes wa; + XSetWindowAttributes swa; XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { + /* add systray icons */ + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if (!(c = (Client *)calloc(1, sizeof(Client)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); + if (!(c->win = cme->data.l[2])) { + free(c); + return; + } + c->mon = selmon; + c->next = systray->icons; + systray->icons = c; + if (!XGetWindowAttributes(dpy, c->win, &wa)) { + /* use sane defaults */ + wa.width = bh; + wa.height = bh; + wa.border_width = 0; + } + c->x = c->oldx = c->y = c->oldy = 0; + c->w = c->oldw = wa.width; + c->h = c->oldh = wa.height; + c->oldbw = wa.border_width; + c->bw = 0; + c->isfloating = True; + /* reuse tags field as mapped status */ + c->tags = 1; + updatesizehints(c); + updatesystrayicongeom(c, wa.width, wa.height); + XAddToSaveSet(dpy, c->win); + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); + XReparentWindow(dpy, c->win, systray->win, 0, 0); + /* use parents background color */ + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + /* FIXME not sure if I have to send these events, too */ + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + XSync(dpy, False); + resizebarwin(selmon); + updatesystray(); + setclientstate(c, NormalState); + } + return; + } + if (!c) return; if (cme->message_type == netatom[NetWMState]) { @@ -569,7 +657,7 @@ configurenotify(XEvent *e) for (c = m->clients; c; c = c->next) if (c->isfullscreen) resizeclient(c, m->mx, m->my, m->mw, m->mh); - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + resizebarwin(m); } focus(NULL); arrange(NULL); @@ -654,6 +742,11 @@ destroynotify(XEvent *e) if ((c = wintoclient(ev->window))) unmanage(c, 1); + else if ((c = wintosystrayicon(ev->window))) { + removesystrayicon(c); + resizebarwin(selmon); + updatesystray(); + } } void @@ -697,7 +790,7 @@ dirtomon(int dir) void drawbar(Monitor *m) { - int x, w, tw = 0; + int x, w, tw = 0, stw = 0; int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; unsigned int i, occ = 0, urg = 0; @@ -706,13 +799,17 @@ drawbar(Monitor *m) if (!m->showbar) return; + if(showsystray && m == systraytomon(m) && !systrayonleft) + stw = getsystraywidth(); + /* draw status first so it can be overdrawn by tags later */ if (m == selmon) { /* status is only drawn on selected monitor */ drw_setscheme(drw, scheme[SchemeNorm]); - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px extra right padding */ + drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0); } + resizebarwin(m); for (c = m->clients; c; c = c->next) { occ |= c->tags; if (c->isurgent) @@ -733,7 +830,7 @@ drawbar(Monitor *m) drw_setscheme(drw, scheme[SchemeNorm]); x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); - if ((w = m->ww - tw - x) > bh) { + if ((w = m->ww - tw - stw - x) > bh) { if (m->sel) { drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); @@ -744,7 +841,7 @@ drawbar(Monitor *m) drw_rect(drw, x, 0, w, bh, 1, 1); } } - drw_map(drw, m->barwin, 0, 0, m->ww, bh); + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); } void @@ -781,8 +878,11 @@ 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); + if (m == selmon) + updatesystray(); + } } void @@ -868,14 +968,32 @@ getatomprop(Client *c, Atom prop) unsigned char *p = NULL; Atom da, atom = None; - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + Atom req = XA_ATOM; + if (prop == xatom[XembedInfo]) + req = xatom[XembedInfo]; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; + if (da == xatom[XembedInfo] && dl == 2) + atom = ((Atom *)p)[1]; XFree(p); } return atom; } +unsigned int +getsystraywidth() +{ + unsigned int w = 0; + Client *i; + if(showsystray) + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; + return w ? w + systrayspacing : 1; +} + int getrootptr(int *x, int *y) { @@ -1016,7 +1134,8 @@ killclient(const Arg *arg) { if (!selmon->sel) return; - if (!sendevent(selmon->sel, wmatom[WMDelete])) { + + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { XGrabServer(dpy); XSetErrorHandler(xerrordummy); XSetCloseDownMode(dpy, DestroyAll); @@ -1103,6 +1222,13 @@ maprequest(XEvent *e) static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; + Client *i; + if ((i = wintosystrayicon(ev->window))) { + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); + resizebarwin(selmon); + updatesystray(); + } + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) return; if (!wintoclient(ev->window)) @@ -1224,6 +1350,17 @@ propertynotify(XEvent *e) Window trans; XPropertyEvent *ev = &e->xproperty; + if ((c = wintosystrayicon(ev->window))) { + if (ev->atom == XA_WM_NORMAL_HINTS) { + updatesizehints(c); + updatesystrayicongeom(c, c->w, c->h); + } + else + updatesystrayiconstate(c, ev); + resizebarwin(selmon); + updatesystray(); + } + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) updatestatus(); else if (ev->state == PropertyDelete) @@ -1274,6 +1411,19 @@ recttomon(int x, int y, int w, int h) return r; } +void +removesystrayicon(Client *i) +{ + Client **ii; + + if (!showsystray || !i) + return; + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); + if (ii) + *ii = i->next; + free(i); +} + void resize(Client *c, int x, int y, int w, int h, int interact) { @@ -1281,6 +1431,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) resizeclient(c, x, y, w, h); } +void +resizebarwin(Monitor *m) { + unsigned int w = m->ww; + if (showsystray && m == systraytomon(m) && !systrayonleft) + w -= getsystraywidth(); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); +} + void resizeclient(Client *c, int x, int y, int w, int h) { @@ -1296,6 +1454,19 @@ resizeclient(Client *c, int x, int y, int w, int h) XSync(dpy, False); } +void +resizerequest(XEvent *e) +{ + XResizeRequestEvent *ev = &e->xresizerequest; + Client *i; + + if ((i = wintosystrayicon(ev->window))) { + updatesystrayicongeom(i, ev->width, ev->height); + resizebarwin(selmon); + updatesystray(); + } +} + void resizemouse(const Arg *arg) { @@ -1442,26 +1613,37 @@ setclientstate(Client *c, long state) } int -sendevent(Client *c, Atom proto) +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) { int n; - Atom *protocols; + Atom *protocols, mt; int exists = 0; XEvent ev; - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { - while (!exists && n--) - exists = protocols[n] == proto; - XFree(protocols); + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; + if (XGetWMProtocols(dpy, w, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } } + else { + exists = True; + mt = proto; + } + if (exists) { ev.type = ClientMessage; - ev.xclient.window = c->win; - ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.window = w; + ev.xclient.message_type = mt; ev.xclient.format = 32; - ev.xclient.data.l[0] = proto; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, c->win, False, NoEventMask, &ev); + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &ev); } return exists; } @@ -1475,7 +1657,7 @@ setfocus(Client *c) XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); } - sendevent(c, wmatom[WMTakeFocus]); + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); } void @@ -1571,6 +1753,10 @@ setup(void) wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); @@ -1578,6 +1764,9 @@ setup(void) netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); /* init cursors */ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); cursor[CurResize] = drw_cur_create(drw, XC_sizing); @@ -1586,6 +1775,8 @@ setup(void) scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); for (i = 0; i < LENGTH(colors); i++) scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init system tray */ + updatesystray(); /* init bars */ updatebars(); updatestatus(); @@ -1716,7 +1907,18 @@ togglebar(const Arg *arg) { selmon->showbar = !selmon->showbar; updatebarpos(selmon); - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + resizebarwin(selmon); + if (showsystray) { + XWindowChanges wc; + if (!selmon->showbar) + wc.y = -bh; + else if (selmon->showbar) { + wc.y = 0; + if (!selmon->topbar) + wc.y = selmon->mh - bh; + } + XConfigureWindow(dpy, systray->win, CWY, &wc); + } arrange(selmon); } @@ -1812,11 +2014,18 @@ unmapnotify(XEvent *e) else unmanage(c, 0); } + else if ((c = wintosystrayicon(ev->window))) { + /* KLUDGE! sometimes icons occasionally unmap their windows, but do + * _not_ destroy them. We map those windows back */ + XMapRaised(dpy, c->win); + updatesystray(); + } } void updatebars(void) { + unsigned int w; Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, @@ -1827,10 +2036,15 @@ updatebars(void) for (m = mons; m; m = m->next) { if (m->barwin) continue; - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + w = m->ww; + if (showsystray && m == systraytomon(m)) + w -= getsystraywidth(); + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + if (showsystray && m == systraytomon(m)) + XMapRaised(dpy, systray->win); XMapRaised(dpy, m->barwin); XSetClassHint(dpy, m->barwin, &ch); } @@ -2007,6 +2221,125 @@ updatestatus(void) if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) strcpy(stext, "dwm-"VERSION); drawbar(selmon); + updatesystray(); +} + + +void +updatesystrayicongeom(Client *i, int w, int h) +{ + if (i) { + i->h = bh; + if (w == h) + i->w = bh; + else if (h == bh) + i->w = w; + else + i->w = (int) ((float)bh * ((float)w / (float)h)); + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); + /* force icons into the systray dimensions if they don't want to */ + if (i->h > bh) { + if (i->w == i->h) + i->w = bh; + else + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); + i->h = bh; + } + } +} + +void +updatesystrayiconstate(Client *i, XPropertyEvent *ev) +{ + long flags; + int code = 0; + + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || + !(flags = getatomprop(i, xatom[XembedInfo]))) + return; + + if (flags & XEMBED_MAPPED && !i->tags) { + i->tags = 1; + code = XEMBED_WINDOW_ACTIVATE; + XMapRaised(dpy, i->win); + setclientstate(i, NormalState); + } + else if (!(flags & XEMBED_MAPPED) && i->tags) { + i->tags = 0; + code = XEMBED_WINDOW_DEACTIVATE; + XUnmapWindow(dpy, i->win); + setclientstate(i, WithdrawnState); + } + else + return; + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, + systray->win, XEMBED_EMBEDDED_VERSION); +} + +void +updatesystray(void) +{ + XSetWindowAttributes wa; + XWindowChanges wc; + Client *i; + Monitor *m = systraytomon(NULL); + unsigned int x = m->mx + m->mw; + unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; + unsigned int w = 1; + + if (!showsystray) + return; + if (systrayonleft) + x -= sw + lrpad / 2; + if (!systray) { + /* init systray */ + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); + wa.event_mask = ButtonPressMask | ExposureMask; + wa.override_redirect = True; + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XSelectInput(dpy, systray->win, SubstructureNotifyMask); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); + XMapRaised(dpy, systray->win); + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); + XSync(dpy, False); + } + else { + fprintf(stderr, "dwm: unable to obtain system tray.\n"); + free(systray); + systray = NULL; + return; + } + } + for (w = 0, i = systray->icons; i; i = i->next) { + /* make sure the background color stays the same */ + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); + XMapRaised(dpy, i->win); + w += systrayspacing; + i->x = w; + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); + w += i->w; + if (i->mon != m) + i->mon = m; + } + w = w ? w + systrayspacing : 1; + x -= w; + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; + wc.stack_mode = Above; wc.sibling = m->barwin; + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); + XMapWindow(dpy, systray->win); + XMapSubwindows(dpy, systray->win); + /* redraw background */ + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); + XSync(dpy, False); } void @@ -2074,6 +2407,16 @@ wintoclient(Window w) return NULL; } +Client * +wintosystrayicon(Window w) { + Client *i = NULL; + + if (!showsystray || !w) + return i; + for (i = systray->icons; i && i->win != w; i = i->next) ; + return i; +} + Monitor * wintomon(Window w) { @@ -2127,6 +2470,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) return -1; } +Monitor * +systraytomon(Monitor *m) { + Monitor *t; + int i, n; + if(!systraypinning) { + if(!m) + return selmon; + return m == selmon ? m : NULL; + } + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; + if(systraypinningfailfirst && n < systraypinning) + return mons; + return t; +} + void zoom(const Arg *arg) { diff --git a/dwm.c.orig b/dwm.c.orig new file mode 100644 index 0000000..1443802 --- /dev/null +++ b/dwm.c.orig @@ -0,0 +1,2164 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +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, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* 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 topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(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 Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } 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); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + 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); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + + 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->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + int start, end, skip; + KeySym *syms; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + 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) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +quit(const Arg *arg) +{ + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + 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) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +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; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + struct sigaction sa; + + /* do not transform children into zombies when they terminate */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + + /* clean up any zombies (inherited from .xinitrc etc) immediately */ + while (waitpid(-1, NULL, WNOHANG) > 0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +spawn(const Arg *arg) +{ + struct sigaction sa; + + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + 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; +} + +void +updateclientlist(void) +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/dwm.c.rej b/dwm.c.rej new file mode 100644 index 0000000..0aeea48 --- /dev/null +++ b/dwm.c.rej @@ -0,0 +1,16 @@ +--- dwm.c ++++ dwm.c +@@ -1215,6 +1334,13 @@ maprequest(XEvent *e) + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + ++ Client *i; ++ if ((i = wintosystrayicon(ev->window))) { ++ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); ++ resizebarwin(selmon); ++ updatesystray(); ++ } ++ + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) diff --git a/dwm.o b/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..fa6fe0a5aabda6c2549055143361311254dc62a4 GIT binary patch literal 69648 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 bbWZLcZNJq{(<^^we}u)Q%f)hWq51y<7hlF_ literal 0 HcmV?d00001 diff --git a/util.o b/util.o new file mode 100644 index 0000000000000000000000000000000000000000..7294d9e50d690ae9ab51afa8dd1e833ca3986073 GIT binary patch literal 2480 zcmbuA&x;&I6vtohPTV!w%xppo3o+72MQ1>pnS`KhR7Q5!!8wJ}SK^A&UyG|sUl-Su{zi;g zxBSiAu_7BjS6irzer1#GpZ(;=Z>QMYEc%AORXnK2bD3agO%4soG+TVIa6;5uNVK+|#+}O(x6@3G%r7SGP>uH70 zM#DLOOU+4vcfNCv4X7D(yHRtIXnARA(Jd~AJsCx=AH=P&7q;BWNw4CS-F~H{O$?Od zHqUJjx8>K)62I>VY?>Pcw##kT?GUi4yN=3xPI-xrLbLy^{H!JqDwN34@7oW0Fn2S%U2j?mZ}Csr zuey8Gd)l6Z+&O5~QFCT?m>bqBD^D^Q$Gr(0dp9n=Hi5r7fxkO}2NQUkIo`{>k6LSH z=O@I|PB)AZxq5+V+zsK3 z*%`rYd)xh?Y*M)1krbv!m*+6#2En<*mGI|wDhVJ9zQ-AEhF=F{K|J=@@SA`v;5eIx ze-Fq4j&IEH+khIfRfpK)XeTZBN}PgL&p+bZigITYh3;{J^v4PB2y!q|2HInL=*r3 literal 0 HcmV?d00001