dwm

[fork] dynamic window manager
Log | Files | Refs | README | LICENSE

commit be256b3d89258718ba4b680f536ee7ee613cf14d
parent 8af5dd28ac62b5f824e7ce8667daccda3b622a23
Author: hhvn <hayden@haydenvh.com>
Date:   Thu, 26 Nov 2020 20:25:35 +0000

makefile dwm-6.2.diff[NEW]: provide history

Diffstat:
DMakefile | 51---------------------------------------------------
Adwm-6.2.diff | 1111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amakefile | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1165 insertions(+), 51 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,51 +0,0 @@ -# dwm - dynamic window manager -# See LICENSE file for copyright and license details. - -include config.mk - -SRC = drw.c dwm.c util.c -OBJ = ${SRC:.c=.o} - -all: options dwm - -options: - @echo dwm build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" - -.c.o: - ${CC} -c ${CFLAGS} $< - -${OBJ}: config.h config.mk - -config.h: - cp config.def.h $@ - -dwm: ${OBJ} - ${CC} -o $@ ${OBJ} ${LDFLAGS} - -clean: - rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz - -dist: clean - mkdir -p dwm-${VERSION} - cp -R LICENSE Makefile README config.def.h config.mk\ - dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} - tar -cf dwm-${VERSION}.tar dwm-${VERSION} - gzip dwm-${VERSION}.tar - rm -rf dwm-${VERSION} - -install: all - mkdir -p ${DESTDIR}${PREFIX}/bin - cp -f dwm ${DESTDIR}${PREFIX}/bin - chmod 755 ${DESTDIR}${PREFIX}/bin/dwm - mkdir -p ${DESTDIR}${MANPREFIX}/man1 - sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 - chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 - -uninstall: - rm -f ${DESTDIR}${PREFIX}/bin/dwm\ - ${DESTDIR}${MANPREFIX}/man1/dwm.1 - -.PHONY: all options clean dist install uninstall diff --git a/dwm-6.2.diff b/dwm-6.2.diff @@ -0,0 +1,1111 @@ +diff --git a/LICENSE b/LICENSE +index d221f09..a5a021d 100644 +--- a/LICENSE ++++ b/LICENSE +@@ -17,6 +17,7 @@ MIT/X Consortium License + © 2015-2016 Quentin Rameau <quinq@fifth.space> + © 2015-2016 Eric Pruitt <eric.pruitt@gmail.com> + © 2016-2017 Markus Teich <markus.teich@stusta.mhn.de> ++(C) 2019-Present - Forked by Hayden Hamilton <hayden@haydenvh.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), +diff --git a/config.h b/config.h +new file mode 100644 +index 0000000..6ac0d79 +--- /dev/null ++++ b/config.h +@@ -0,0 +1,97 @@ ++/* See LICENSE file for copyright and license details. */ ++ ++/* appearance */ ++static const unsigned int borderpx = 2; /* border pixel of windows */ ++static const unsigned int gappx = 10; ++static const unsigned int snap = 1; /* 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=8" }; ++static const char *colors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { "#bbbbbb", "#0a0a10", "#0a0a10" }, ++ [SchemeSel] = { "#eeeeee", "#30404e", "#892b2b" }, ++ [SchemeStat] = { "#eeeeee", "#0a2126", "#0a2126" }, ++ [SchemeNormWin] = { "#bbbbbb", "#24284c", "#0a0a10" }, ++ [SchemeUrgent] = { "#892b2b", "#0a0a10", "#0a0a10" }, ++}; ++ ++/* tagging */ ++static const char *tags[] = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; ++ ++static const Rule rules[] = { ++ { "Gimp", NULL, NULL, 1 << 7, 0, 0 }, ++ { "mpv", NULL, NULL, 1 << 1, 0, 0 }, ++ { "mpvrcp", NULL, NULL, 1 << 0, 1, 0 }, ++}; ++ ++/* 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 = 0; /* 1 means respect size hints in tiled resizals */ ++ ++static const Layout layouts[] = { ++ /* symbol arrange function */ ++ { "|=", tile }, /* first entry is default */ ++ { ".:", NULL }, /* no layout function means floating behavior */ ++ { "M", monocle }, ++ { "@", spiral }, ++ { "#=", dwindle }, ++ { "=#=", centeredmaster }, ++ { "=:=", centeredfloatingmaster }, ++}; ++ ++/* key definitions */ ++#define MODKEY Mod4Mask ++#define AltMask 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} }, ++#define LAYOUT(KEY, LAYOUT) \ ++ { MODKEY|AltMask, KEY, setlayout, {.v = &layouts[LAYOUT]} }, ++ ++/* helper for spawning shell commands in the pre dwm-5.0 fashion */ ++#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } ++ ++static Key keys[] = { ++ /* modifier key function argument */ ++ { MODKEY|ShiftMask, XK_Return, zoom, {0} }, ++ { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, ++ { MODKEY, XK_Tab, view, {0} }, ++ { MODKEY, XK_f, togglefullscr, {0} }, ++ { MODKEY, XK_h, setmfact, {.f = -0.05} }, ++ { MODKEY, XK_i, incnmaster, {.i = +1 } }, ++ { MODKEY, XK_j, focusstack, {.i = +1 } }, ++ { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY, XK_l, setmfact, {.f = +0.05 } }, ++ { MODKEY, XK_p, incnmaster, {.i = -1 } }, ++ { MODKEY, XK_q, killclient, {0} }, ++ { MODKEY|ShiftMask, XK_q, quit, {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 } }, ++ /* Layout */ ++ LAYOUT( XK_1, 0) ++ LAYOUT( XK_2, 1) ++ LAYOUT( XK_3, 3) ++ LAYOUT( XK_4, 4) ++ LAYOUT( XK_5, 5) ++ LAYOUT( XK_6, 6) ++ LAYOUT( XK_7, 7) ++ LAYOUT( XK_0, 2) ++ /* Tags */ ++ 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) ++}; +diff --git a/dwm.c b/dwm.c +index 4465af1..09f86e7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -30,12 +30,14 @@ + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> ++#include <sys/stat.h> + #include <X11/cursorfont.h> + #include <X11/keysym.h> + #include <X11/Xatom.h> + #include <X11/Xlib.h> + #include <X11/Xproto.h> + #include <X11/Xutil.h> ++#include <X11/Xft/Xft.h> + #ifdef XINERAMA + #include <X11/extensions/Xinerama.h> + #endif /* XINERAMA */ +@@ -49,8 +51,15 @@ + #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) ++#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags]) + #define LENGTH(X) (sizeof X / sizeof X[0]) ++#ifndef MAX ++#define MAX(A, B) ((A) > (B) ? (A) : (B)) ++#endif ++#ifndef MIN ++#define MIN(A, B) ((A) < (B) ? (A) : (B)) ++#endif + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +@@ -59,13 +68,11 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeStat, SchemeNormWin, SchemeUrgent }; /* 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; +@@ -77,7 +84,6 @@ typedef union { + typedef struct { + unsigned int click; + unsigned int mask; +- unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; + } Button; +@@ -117,8 +123,11 @@ struct Monitor { + int nmaster; + int num; + int by; /* bar geometry */ ++ int btw; /* width of tasks portion of bar */ ++ int bt; /* number of tasks */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ ++ int gappx; /* gaps between windows */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; +@@ -147,8 +156,8 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachaside(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); +@@ -163,6 +172,7 @@ static void detachstack(Client *c); + static Monitor *dirtomon(int dir); + static void drawbar(Monitor *m); + static void drawbars(void); ++static int drawstatusbar(Monitor *m, int bh, char* text); + static void enternotify(XEvent *e); + static void expose(XEvent *e); + static void focus(Client *c); +@@ -172,7 +182,6 @@ static void focusstack(const Arg *arg); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +-static void grabbuttons(Client *c, int focused); + static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); +@@ -182,7 +191,7 @@ 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 *nexttagged(Client *c); + static Client *nexttiled(Client *c); + static void pop(Client *); + static void propertynotify(XEvent *e); +@@ -190,7 +199,6 @@ 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); +@@ -199,6 +207,7 @@ 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 setgaps(const Arg *arg); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -209,8 +218,9 @@ static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *); +-static void togglebar(const Arg *arg); ++//static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglefullscr(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -233,18 +243,24 @@ 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); ++static void centeredmaster(Monitor *m); ++static void centeredfloatingmaster(Monitor *m); ++static void spiral(Monitor *mon); ++static void dwindle(Monitor *mon); ++static void fibonacci(Monitor *mon, int s); + + /* variables */ + static const char broken[] = "broken"; +-static char stext[256]; ++static char stext[1024]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ ++static int sidepad; ++static int vertpad; + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +- [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, +@@ -407,52 +423,21 @@ attach(Client *c) + } + + void +-attachstack(Client *c) +-{ +- c->snext = c->mon->stack; +- c->mon->stack = c; ++attachaside(Client *c) { ++ Client *at = nexttagged(c); ++ if(!at) { ++ attach(c); ++ return; ++ } ++ c->next = at->next; ++ at->next = c; + } + + void +-buttonpress(XEvent *e) ++attachstack(Client *c) + { +- 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 + blw) +- click = ClkLtSymbol; +- else if (ev->x > selmon->ww - 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); ++ c->snext = c->mon->stack; ++ c->mon->stack = c; + } + + void +@@ -484,7 +469,7 @@ cleanup(void) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); +- for (i = 0; i < LENGTH(colors); i++) ++ for (i = 0; i < LENGTH(colors) + 1; i++) + free(scheme[i]); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); +@@ -567,7 +552,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); ++ XMoveResizeWindow(dpy, m->barwin, m->wx + sidepad, m->by + vertpad, m->ww - 2*sidepad, bh); + } + focus(NULL); + arrange(NULL); +@@ -638,6 +623,7 @@ createmon(void) + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; ++ m->gappx = gappx; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -692,23 +678,129 @@ dirtomon(int dir) + return m; + } + ++int ++drawstatusbar(Monitor *m, int bh, char* stext) { ++ int ret, i, w, x, len; ++ short isCode = 0; ++ char *text; ++ char *p; ++ ++ len = strlen(stext) + 1 ; ++ if (!(text = (char*) malloc(sizeof(char)*len))) ++ die("malloc"); ++ p = text; ++ memcpy(text, stext, len); ++ ++ /* compute width of the status text */ ++ w = 0; ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^') { ++ if (!isCode) { ++ isCode = 1; ++ text[i] = '\0'; ++ w += TEXTW(text) - lrpad; ++ text[i] = '^'; ++ if (text[++i] == 'f') ++ w += atoi(text + ++i); ++ } else { ++ isCode = 0; ++ text = text + i + 1; ++ i = -1; ++ } ++ } ++ } ++ if (!isCode) ++ w += TEXTW(text) - lrpad; ++ else ++ isCode = 0; ++ text = p; ++ ++ w += 2; /* 1px padding on both sides */ ++ ret = x = m->ww - w; ++ ++ drw_setscheme(drw, scheme[LENGTH(colors)]); ++ drw->scheme[ColFg] = scheme[SchemeStat][ColFg]; ++ drw_rect(drw, x, 0, w, bh, 1, 1); ++ x++; ++ ++ /* process status text */ ++ i = -1; ++ while (text[++i]) { ++ if (text[i] == '^' && !isCode) { ++ isCode = 1; ++ ++ text[i] = '\0'; ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ ++ x += w; ++ ++ /* process code */ ++ while (text[++i] != '^') { ++ if (text[i] == 'c') { ++ char buf[8]; ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ drw_clr_create(drw, &drw->scheme[ColFg], buf); ++ i += 7; ++ } else if (text[i] == 'b') { ++ char buf[8]; ++ memcpy(buf, (char*)text+i+1, 7); ++ buf[7] = '\0'; ++ drw_clr_create(drw, &drw->scheme[ColBg], buf); ++ i += 7; ++ } else if (text[i] == 'd') { ++ drw->scheme[ColFg] = scheme[SchemeStat][ColFg]; ++ drw->scheme[ColBg] = scheme[SchemeStat][ColBg]; ++ } else if (text[i] == 'r') { ++ int rx = atoi(text + ++i); ++ while (text[++i] != ','); ++ int ry = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rw = atoi(text + ++i); ++ while (text[++i] != ','); ++ int rh = atoi(text + ++i); ++ ++ drw_rect(drw, rx + x, ry, rw, rh, 1, 0); ++ } else if (text[i] == 'f') { ++ x += atoi(text + ++i); ++ } ++ } ++ ++ text = text + i + 1; ++ i=-1; ++ isCode = 0; ++ } ++ } ++ ++ drw->scheme[ColBg] = scheme[SchemeStat][ColBg]; ++ if (!isCode) { ++ w = TEXTW(text) - lrpad; ++ drw_text(drw, x, 0, w, bh, 0, text, 0); ++ } ++ ++ free(p); ++ ++ return ret; ++} ++ + void + drawbar(Monitor *m) + { +- int x, w, sw = 0; ++ int x, w, sw = 0, n = 0, scm; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + /* 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]); +- sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); +- } ++ drw_setscheme(drw, scheme[SchemeSel]); ++ sw = m->ww - drawstatusbar(m, bh, stext); + + for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) ++ n++; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; +@@ -716,10 +808,13 @@ drawbar(Monitor *m) + 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]); ++ if (urg & 1 << i) ++ drw_setscheme(drw, scheme[SchemeUrgent]); ++ else ++ 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, ++ drw_rect(drw, x + 2*boxs, 9*boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; +@@ -729,16 +824,42 @@ drawbar(Monitor *m) + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - sw - 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); ++ if (n > 0) { ++ int remainder = w % n; ++ int tabw = (1.0 / (double)n) * w + 1; ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (m->sel == c && m == selmon) ++ scm = SchemeSel; ++ else ++ scm = SchemeNormWin; ++ drw_setscheme(drw, scheme[scm]); ++ ++ if (remainder >= 0) { ++ if (remainder == 0) { ++ tabw--; ++ } ++ remainder--; ++ } ++ drw_text(drw, x, 0, tabw - borderpx, bh, lrpad / 2, c->name, 0); ++ /* draw seperator */ ++ drw_setscheme(drw, scheme[SchemeNorm]); ++ drw_rect(drw, x + tabw - borderpx, 0, borderpx, bh, 1, 1); ++ ++ x += tabw; ++ } + } else { +- drw_setscheme(drw, scheme[SchemeNorm]); ++ if (m == selmon) ++ drw_setscheme(drw, scheme[SchemeNormWin]); ++ else ++ drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } ++ ++ m->bt = n; ++ m->btw = w; + drw_map(drw, m->barwin, 0, 0, m->ww, bh); + } + +@@ -794,7 +915,6 @@ focus(Client *c) + seturgent(c, 0); + detachstack(c); + attachstack(c); +- grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { +@@ -924,27 +1044,6 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size) + 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) + { +@@ -1057,12 +1156,11 @@ manage(Window w, XWindowAttributes *wa) + 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); ++ attachaside(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1110,9 +1208,9 @@ monocle(Monitor *m) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ +- snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); ++ snprintf(m->ltsymbol, sizeof m->ltsymbol, "M%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); ++ resize(c, m->wx + m->gappx, m->wy + m->gappx, (m->ww - 2 * c->bw) - 2*m->gappx, (m->wh - 2 * c->bw) - 2*m->gappx, 0); + } + + void +@@ -1132,64 +1230,14 @@ motionnotify(XEvent *e) + 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 * ++nexttagged(Client *c) { ++ Client *walked = c->mon->clients; ++ for(; ++ walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); ++ walked = walked->next ++ ); ++ return walked; + } + + Client * +@@ -1287,71 +1335,12 @@ resizeclient(Client *c, int x, int y, int w, int h) + 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); ++ XWindowChanges wc; drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) +@@ -1417,7 +1406,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1497,6 +1486,18 @@ setfullscreen(Client *c, int fullscreen) + } + } + ++/* ++void ++setgaps(const Arg *arg) ++{ ++ if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) ++ selmon->gappx = 0; ++ else ++ selmon->gappx += arg->i; ++ arrange(selmon); ++} ++*/ ++ + void + setlayout(const Arg *arg) + { +@@ -1563,16 +1564,20 @@ setup(void) + 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[CurNormal] = drw_cur_create(drw, XC_sailboat); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ +- scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); ++ scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); ++ scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ ++ vertpad = gappx; ++ sidepad = gappx; + updatebars(); + updatestatus(); ++ updatebarpos(selmon); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, +@@ -1639,8 +1644,6 @@ sigchld(int unused) + void + spawn(const Arg *arg) + { +- if (arg->v == dmenucmd) +- dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); +@@ -1683,28 +1686,19 @@ tile(Monitor *m) + 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++) ++ mw = m->ww - m->gappx; ++ for (i = 0, my = ty = m->gappx, 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); +- my += HEIGHT(c); ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; ++ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); ++ my += HEIGHT(c) + m->gappx; + } 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); +- ty += HEIGHT(c); ++ h = (m->wh - ty) / (n - i) - m->gappx; ++ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - (2*m->gappx), h - (2*c->bw), 0); ++ ty += HEIGHT(c) + m->gappx; + } + } + +-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) + { +@@ -1719,6 +1713,13 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglefullscr(const Arg *arg) ++{ ++ if(selmon->sel) ++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen); ++} ++ + void + toggletag(const Arg *arg) + { +@@ -1751,7 +1752,6 @@ 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); +@@ -1811,7 +1811,7 @@ 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), ++ m->barwin = XCreateWindow(dpy, root, m->wx + sidepad, m->by + vertpad, m->ww - 2*sidepad, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +@@ -1826,11 +1826,11 @@ 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; ++ m->wh = m->wh - vertpad - bh; ++ m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; ++ m->wy = m->topbar ? m->wy + bh + vertpad : m->wy; + } else +- m->by = -bh; ++ m->by = -bh - vertpad; + } + + void +@@ -1897,7 +1897,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) +@@ -1989,7 +1989,7 @@ updatestatus(void) + { + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ drawbars(); + } + + void +@@ -2147,3 +2147,174 @@ main(int argc, char *argv[]) + XCloseDisplay(dpy); + return EXIT_SUCCESS; + } ++ ++static void ++fibonacci(Monitor *mon, int s) { ++ unsigned int i, n, nx, ny, nw, nh; ++ Client *c; ++ ++ for(n = 0, c = nexttiled(mon->clients); c; c = nexttiled(c->next), n++); ++ if(n == 0) ++ return; ++ ++ nx = mon->wx; ++ ny = 0; ++ nw = mon->ww; ++ nh = mon->wh - gappx; ++ ++ for(i = 0, c = nexttiled(mon->clients); c; c = nexttiled(c->next)) { ++ if((i % 2 && nh / 2 > 2 * c->bw) ++ || (!(i % 2) && nw / 2 > 2 * c->bw)) { ++ if(i < n - 1) { ++ if(i % 2) ++ nh /= 2; ++ else ++ nw /= 2; ++ if((i % 4) == 2 && !s) ++ nx += nw; ++ else if((i % 4) == 3 && !s) ++ ny += nh; ++ } ++ if((i % 4) == 0) { ++ if(s) ++ ny += nh; ++ else ++ ny -= nh; ++ } ++ else if((i % 4) == 1) ++ nx += nw; ++ else if((i % 4) == 2) ++ ny += nh; ++ else if((i % 4) == 3) { ++ if(s) ++ nx += nw; ++ else ++ nx -= nw; ++ } ++ if(i == 0) ++ { ++ if(n != 1) ++ nw = mon->ww * mon->mfact; ++ ny = mon->wy; ++ } ++ else if(i == 1) ++ nw = mon->ww - nw - gappx; ++ i++; ++ } ++ resize(c, nx + mon->gappx, ny + mon->gappx, nw - (2 * c->bw) - mon->gappx, nh - 2*c->bw - mon->gappx, False); ++ } ++} ++ ++static void ++dwindle(Monitor *mon) { ++ fibonacci(mon, 1); ++} ++ ++static void ++spiral(Monitor *mon) { ++ fibonacci(mon, 0); ++} ++ ++void ++centeredmaster(Monitor *m) ++{ ++ unsigned int i, n, h, mw, mx, my, oty, ety, tw; ++ Client *c; ++ ++ /* count number of clients in the selected monitor */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mw = m->ww; ++ mx = 0; ++ my = 0; ++ tw = mw; ++ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ tw = m->ww - mw; ++ ++ if (n - m->nmaster > 1) { ++ /* only one client */ ++ mx = (m->ww - mw) / 2; ++ tw = (m->ww - mw) / 2; ++ } ++ } ++ ++ oty = 0; ++ ety = 0; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center ++ * of the screen */ ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx + mx + gappx, m->wy + my + gappx, (mw - (2*c->bw)) - 2*gappx, ++ (h - (2*c->bw)) - 2*gappx, 0); ++ my += HEIGHT(c); ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2 ) { ++ h = (m->wh - ety) / ( (1 + n - i) / 2); ++ resize(c, m->wx + gappx, m->wy + ety + gappx, tw - (2*c->bw) - gappx, ++ (h - (2*c->bw)) - 2*gappx, 0); ++ ety += HEIGHT(c); ++ } else { ++ h = (m->wh - oty) / ((1 + n - i) / 2); ++ resize(c, m->wx + mx + mw, m->wy + oty + gappx, ++ tw - (2*c->bw) - gappx, (h - (2*c->bw)) - 2*gappx, 0); ++ oty += HEIGHT(c); ++ } ++ } ++} ++ ++void ++centeredfloatingmaster(Monitor *m) ++{ ++ unsigned int i, n, w, mh, mw, mx, mxo, my, myo, tx; ++ Client *c; ++ ++ /* count number of clients in the selected monitor */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ /* initialize nmaster area */ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ if (m->ww > m->wh) { ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ mh = m->nmaster ? m->wh * 0.9 : 0; ++ } else { ++ mh = m->nmaster ? m->wh * m->mfact : 0; ++ mw = m->nmaster ? m->ww * 0.9 : 0; ++ } ++ mx = mxo = (m->ww - mw) / 2; ++ my = myo = (m->wh - mh) / 2; ++ } else { ++ /* go fullscreen if all clients are in the master area */ ++ mh = m->wh - 2*gappx; ++ mw = m->ww - 2*gappx; ++ mx = mxo = 0 + gappx; ++ my = myo = 0 + gappx; ++ } ++ ++ for(i = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked horizontally, in the center ++ * of the screen */ ++ w = (mw + mxo - mx) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx + mx, m->wy + my, w - (2*c->bw), ++ mh - (2*c->bw), 0); ++ mx += WIDTH(c) + gappx; ++ } else { ++ /* stack clients are stacked horizontally */ ++ w = (m->ww - tx) / (n - i); ++ resize(c, m->wx + tx + gappx, m->wy + gappx, w - (2*c->bw) - 2*gappx, ++ m->wh - (2*c->bw) - 2*gappx, 0); ++ tx += WIDTH(c) + gappx; ++ } ++ /* XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, (unsigned char *)&netatom[NetWMFullscreen], 1); */ ++} diff --git a/makefile b/makefile @@ -0,0 +1,54 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +diff: + git diff cb3f58a -- `ls | sed '/dwm-6.2.diff/d'` > dwm-6.2.diff + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall