Compare commits

...

12 Commits

Author SHA1 Message Date
b5d22bd962 applied actual-fullscreen patch 2025-05-19 16:08:45 -03:00
2e2c9d12fa changed MODKEY from alt to meta 2025-05-19 16:08:45 -03:00
681199d175 applied hardware volume keys patch 2025-05-19 16:08:45 -03:00
62af801ea4 added movekeyboard patch 2025-05-19 16:08:45 -03:00
2944e7dec6 applied focusonclick patch 2025-05-19 16:08:45 -03:00
49f6cb939f applied gridmode patch 2025-05-19 16:08:45 -03:00
b6089785c7 applied resizecorners patch 2025-05-19 16:08:45 -03:00
241f837e6b applied adjacenttag-spikvacant patch 2025-05-19 16:08:45 -03:00
41e4daf644 applied hide vacant tags patch 2025-05-19 16:08:45 -03:00
Raymond Cole
cfb8627a80 Avoid unsigned integer underflow in drw_text() 2024-10-30 13:02:17 +01:00
Hiltjo Posthuma
fcb2476b69 util.c: output function might override errno and thus affect perror()
Original patch by Raymond Cole with some modifications, thanks!
2024-10-27 20:10:07 +01:00
Hiltjo Posthuma
8933ebcf50 sync drw.{c,h} from dmenu
- drw: minor improvement to the nomatches cache
- overhaul utf8decoding and render invalid utf8 sequences as U+FFFD.

Thanks NRK for these improvements!
2024-10-05 13:06:08 +02:00
7 changed files with 348 additions and 121 deletions

View File

@ -5,6 +5,7 @@ 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 int focusonwheel = 0;
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
@ -31,6 +32,7 @@ static const Rule rules[] = {
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
};
#include "layouts.c"
/* 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 */
@ -42,10 +44,11 @@ static const Layout layouts[] = {
{ "[]=", tile }, /* first entry is default */
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
{ "HHH", grid },
};
/* key definitions */
#define MODKEY Mod1Mask
#define MODKEY Mod4Mask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
@ -55,15 +58,26 @@ static const Layout layouts[] = {
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
#include <X11/XF86keysym.h>
/* 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 };
/* alsa volume control */
static const char *alsacmd[] = { "st", "-c", "floatalsamxr", "-e", "alsamixer", NULL };
static const char *upvol[] = { "amixer", "set", "Master", "2+", NULL };
static const char *downvol[] = { "amixer", "set", "Master", "2-", NULL };
/* alsa muting/unmuting */
static const char *mute[] = { "amixer", "-q", "set", "Master", "toggle", NULL };
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ 0, XF86XK_AudioRaiseVolume, spawn, {.v = upvol } },
{ 0, XF86XK_AudioLowerVolume, spawn, {.v = downvol } },
{ 0, XF86XK_AudioMute, spawn, {.v = mute } },
{ MODKEY, XK_b, togglebar, {0} },
{ MODKEY, XK_j, focusstack, {.i = +1 } },
{ MODKEY, XK_k, focusstack, {.i = -1 } },
@ -77,14 +91,24 @@ static const Key keys[] = {
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XK_g, setlayout, {.v = &layouts[3]} },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
{ MODKEY|ShiftMask, XK_f, togglefullscr, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
{ MODKEY|ControlMask, XK_l, movekeyboard_x, {.i = 20}},
{ MODKEY|ControlMask, XK_h, movekeyboard_x, {.i = -20}},
{ MODKEY|ControlMask, XK_j, movekeyboard_y, {.i = 20}},
{ MODKEY|ControlMask, XK_k, movekeyboard_y, {.i = -20}},
{ MODKEY, XK_Right, viewnext, {0} },
{ MODKEY, XK_Left, viewprev, {0} },
{ MODKEY|ShiftMask, XK_Right, tagtonext, {0} },
{ MODKEY|ShiftMask, XK_Left, tagtoprev, {0} },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
@ -105,6 +129,7 @@ static const Button buttons[] = {
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkStatusText, 0, Button1, spawn, {.v = alsacmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },

116
drw.c
View File

@ -9,54 +9,40 @@
#include "util.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long
utf8decodebyte(const char c, size_t *i)
static int
utf8decode(const char *s_in, long *u, int *err)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
static const unsigned char lens[] = {
/* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
/* 110XX */ 2, 2, 2, 2,
/* 1110X */ 3, 3,
/* 11110 */ 4,
/* 11111 */ 0, /* invalid */
};
static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
const unsigned char *s = (const unsigned char *)s_in;
int len = lens[*s >> 3];
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
*err = 1;
if (len == 0)
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
long cp = s[0] & leading_mask[len - 1];
for (int i = 1; i < len; ++i) {
if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
return i;
cp = (cp << 6) | (s[i] & 0x3F);
}
/* out of range, surrogate, overlong encoding */
if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
return len;
*err = 0;
*u = cp;
return len;
}
@ -238,11 +224,11 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{
int i, ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
int ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
int utf8strlen, utf8charlen, render = x || y || w || h;
int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
@ -251,9 +237,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
XftResult result;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
enum { nomatches_len = 64 };
static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
static unsigned int ellipsis_width = 0;
static unsigned int nomatches[128], ellipsis_width, invalid_width;
static const char invalid[] = "<EFBFBD>";
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
@ -263,6 +248,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
if (w < lpad)
return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
@ -273,12 +260,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
if (!invalid_width && render)
invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) {
ew = ellipsis_len = utf8strlen = 0;
ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
@ -300,9 +289,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
ew += tmpw;
utf8strlen += utf8err ? 0 : utf8charlen;
ew += utf8err ? 0 : tmpw;
} else {
nextfont = curfont;
}
@ -310,7 +299,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
}
if (overflow || !charexists || nextfont)
if (overflow || !charexists || nextfont || utf8err)
break;
else
charexists = 0;
@ -325,6 +314,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
x += ew;
w -= ew;
}
if (utf8err && (!render || invalid_width < w)) {
if (render)
drw_text(drw, x, y, w, h, 0, invalid, invert);
x += invalid_width;
w -= invalid_width;
}
if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
@ -338,11 +333,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* character must be drawn. */
charexists = 1;
for (i = 0; i < nomatches_len; ++i) {
/* avoid calling XftFontMatch if we know we won't find a match */
if (utf8codepoint == nomatches.codepoint[i])
goto no_match;
}
hash = (unsigned int)utf8codepoint;
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
h1 = (hash >> 17) % LENGTH(nomatches);
/* avoid expensive XftFontMatch call when we know we won't find a match */
if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
goto no_match;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
@ -371,7 +369,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont;
} else {
xfont_free(usedfont);
nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}

3
dwm.1
View File

@ -116,6 +116,9 @@ Zooms/cycles focused window to/from master area (tiled layouts only).
.B Mod1\-Shift\-c
Close focused window.
.TP
.B Mod1\-Shift\-f
Toggle fullscreen for focused window.
.TP
.B Mod1\-Shift\-space
Toggle focused window between tiled and floating state.
.TP

282
dwm.c
View File

@ -50,7 +50,6 @@
#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 LENGTH(X) (sizeof X / sizeof X[0])
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
@ -163,7 +162,6 @@ 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);
@ -182,10 +180,13 @@ 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 void movekeyboard_x(const Arg *arg);
static void movekeyboard_y(const Arg *arg);
static unsigned int nexttag(void);
static Client *nexttiled(Client *c);
static void pop(Client *c);
static unsigned int prevtag(void);
static void propertynotify(XEvent *e);
static void quit(const Arg *arg);
static Monitor *recttomon(int x, int y, int w, int h);
@ -208,9 +209,12 @@ 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 tagtonext(const Arg *arg);
static void tagtoprev(const Arg *arg);
static void tile(Monitor *m);
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);
@ -227,6 +231,8 @@ static void updatetitle(Client *c);
static void updatewindowtype(Client *c);
static void updatewmhints(Client *c);
static void view(const Arg *arg);
static void viewnext(const Arg *arg);
static void viewprev(const Arg *arg);
static Client *wintoclient(Window w);
static Monitor *wintomon(Window w);
static int xerror(Display *dpy, XErrorEvent *ee);
@ -249,13 +255,11 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[ConfigureRequest] = configurerequest,
[ConfigureNotify] = configurenotify,
[DestroyNotify] = destroynotify,
[EnterNotify] = enternotify,
[Expose] = expose,
[FocusIn] = focusin,
[KeyPress] = keypress,
[MappingNotify] = mappingnotify,
[MapRequest] = maprequest,
[MotionNotify] = motionnotify,
[PropertyNotify] = propertynotify,
[UnmapNotify] = unmapnotify
};
@ -426,16 +430,23 @@ buttonpress(XEvent *e)
click = ClkRootWin;
/* focus monitor if necessary */
if ((m = wintomon(ev->window)) && m != selmon) {
if ((m = wintomon(ev->window)) && m != selmon
&& (focusonwheel || (ev->button != Button4 && ev->button != Button5))) {
unfocus(selmon->sel, 1);
selmon = m;
focus(NULL);
}
if (ev->window == selmon->barwin) {
i = x = 0;
do
unsigned int occ = 0;
for(c = m->clients; c; c=c->next)
occ |= c->tags;
do {
/* Do not reserve space for vacant tags */
if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
continue;
x += TEXTW(tags[i]);
while (ev->x >= x && ++i < LENGTH(tags));
} while (ev->x >= x && ++i < LENGTH(tags));
if (i < LENGTH(tags)) {
click = ClkTagBar;
arg.ui = 1 << i;
@ -446,8 +457,8 @@ buttonpress(XEvent *e)
else
click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) {
focus(c);
restack(selmon);
if (focusonwheel || (ev->button != Button4 && ev->button != Button5))
focus(c);
XAllowEvents(dpy, ReplayPointer, CurrentTime);
click = ClkClientWin;
}
@ -721,13 +732,12 @@ drawbar(Monitor *m)
}
x = 0;
for (i = 0; i < LENGTH(tags); i++) {
/* Do not draw vacant tags */
if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
continue;
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);
@ -757,25 +767,6 @@ drawbars(void)
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)
{
@ -1125,23 +1116,6 @@ monocle(Monitor *m)
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)
{
@ -1202,6 +1176,115 @@ movemouse(const Arg *arg)
}
}
unsigned int
nexttag(void)
{
unsigned int seltag = selmon->tagset[selmon->seltags];
unsigned int usedtags = 0;
Client *c = selmon->clients;
if (!c)
return seltag;
/* skip vacant tags */
do {
usedtags |= c->tags;
c = c->next;
} while (c);
do {
seltag = seltag == (1 << (LENGTH(tags) - 1)) ? 1 : seltag << 1;
} while (!(seltag & usedtags));
return seltag;
}
void
movekeyboard_x(const Arg *arg){
int ocx, ocy, nx, ny;
Client *c;
Monitor *m;
if (!(c = selmon->sel))
return;
if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
return;
restack(selmon);
ocx = c->x;
ocy = c->y;
nx = ocx + arg->i;
ny = ocy;
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)
togglefloating(NULL);
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
resize(c, nx, ny, c->w, c->h, 1);
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
selmon = m;
focus(NULL);
}
}
void
movekeyboard_y(const Arg *arg){
int ocx, ocy, nx, ny;
Client *c;
Monitor *m;
if (!(c = selmon->sel))
return;
if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
return;
restack(selmon);
ocx = c->x;
ocy = c->y;
nx = ocx;
ny = ocy + arg->i;
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)
togglefloating(NULL);
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
resize(c, nx, ny, c->w, c->h, 1);
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
selmon = m;
focus(NULL);
}
}
Client *
nexttiled(Client *c)
{
@ -1218,6 +1301,28 @@ pop(Client *c)
arrange(c->mon);
}
unsigned int
prevtag(void)
{
unsigned int seltag = selmon->tagset[selmon->seltags];
unsigned int usedtags = 0;
Client *c = selmon->clients;
if (!c)
return seltag;
/* skip vacant tags */
do {
usedtags |= c->tags;
c = c->next;
} while (c);
do {
seltag = seltag == 1 ? (1 << (LENGTH(tags) - 1)) : seltag >> 1;
} while (!(seltag & usedtags));
return seltag;
}
void
propertynotify(XEvent *e)
{
@ -1301,9 +1406,14 @@ void
resizemouse(const Arg *arg)
{
int ocx, ocy, nw, nh;
int ocx2, ocy2, nx, ny;
Client *c;
Monitor *m;
XEvent ev;
int horizcorner, vertcorner;
int di;
unsigned int dui;
Window dummy;
Time lasttime = 0;
if (!(c = selmon->sel))
@ -1313,10 +1423,18 @@ resizemouse(const Arg *arg)
restack(selmon);
ocx = c->x;
ocy = c->y;
ocx2 = c->x + c->w;
ocy2 = c->y + c->h;
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);
if (!XQueryPointer (dpy, c->win, &dummy, &dummy, &di, &di, &nx, &ny, &dui))
return;
horizcorner = nx < c->w / 2;
vertcorner = ny < c->h / 2;
XWarpPointer (dpy, None, c->win, 0, 0, 0, 0,
horizcorner ? (-c->bw) : (c->w + c->bw -1),
vertcorner ? (-c->bw) : (c->h + c->bw -1));
do {
XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
switch(ev.type) {
@ -1330,8 +1448,11 @@ resizemouse(const Arg *arg)
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);
nx = horizcorner ? ev.xmotion.x : c->x;
ny = vertcorner ? ev.xmotion.y : c->y;
nw = MAX(horizcorner ? (ocx2 - nx) : (ev.xmotion.x - ocx - 2 * c->bw + 1), 1);
nh = MAX(vertcorner ? (ocy2 - ny) : (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)
{
@ -1340,11 +1461,13 @@ resizemouse(const Arg *arg)
togglefloating(NULL);
}
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
resize(c, c->x, c->y, nw, nh, 1);
resize(c, nx, ny, 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);
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
horizcorner ? (-c->bw) : (c->w + c->bw - 1),
vertcorner ? (-c->bw) : (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) {
@ -1684,6 +1807,36 @@ tagmon(const Arg *arg)
sendmon(selmon->sel, dirtomon(arg->i));
}
void
tagtonext(const Arg *arg)
{
unsigned int tmp;
if (selmon->sel == NULL)
return;
if ((tmp = nexttag()) == selmon->tagset[selmon->seltags])
return;
tag(&(const Arg){.ui = tmp });
view(&(const Arg){.ui = tmp });
}
void
tagtoprev(const Arg *arg)
{
unsigned int tmp;
if (selmon->sel == NULL)
return;
if ((tmp = prevtag()) == selmon->tagset[selmon->seltags])
return;
tag(&(const Arg){.ui = tmp });
view(&(const Arg){.ui = tmp });
}
void
tile(Monitor *m)
{
@ -1735,6 +1888,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)
{
@ -2062,6 +2222,18 @@ view(const Arg *arg)
arrange(selmon);
}
void
viewnext(const Arg *arg)
{
view(&(const Arg){.ui = nexttag()});
}
void
viewprev(const Arg *arg)
{
view(&(const Arg){.ui = prevtag()});
}
Client *
wintoclient(Window w)
{

27
layouts.c Normal file
View File

@ -0,0 +1,27 @@
void
grid(Monitor *m) {
unsigned int i, n, cx, cy, cw, ch, aw, ah, cols, rows;
Client *c;
for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next))
n++;
/* grid dimensions */
for(rows = 0; rows <= n/2; rows++)
if(rows*rows >= n)
break;
cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows;
/* window geoms (cell height/width) */
ch = m->wh / (rows ? rows : 1);
cw = m->ww / (cols ? cols : 1);
for(i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
cx = m->wx + (i / rows) * cw;
cy = m->wy + (i % rows) * ch;
/* adjust height/width of last row/column's windows */
ah = ((i + 1) % rows == 0) ? m->wh - ch * rows : 0;
aw = (i >= rows * (cols - 1)) ? m->ww - cw * cols : 0;
resize(c, cx, cy, cw - 2 * c->bw + aw, ch - 2 * c->bw + ah, False);
i++;
}
}

13
util.c
View File

@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -10,17 +11,17 @@ void
die(const char *fmt, ...)
{
va_list ap;
int saved_errno;
saved_errno = errno;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
if (fmt[0] && fmt[strlen(fmt)-1] == ':')
fprintf(stderr, " %s", strerror(saved_errno));
fputc('\n', stderr);
exit(1);
}

1
util.h
View File

@ -3,6 +3,7 @@
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);