/* macros */
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
-#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask))
+#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH))
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#define LENGTH(X) (sizeof X / sizeof X[0])
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { ColBorder, ColFG, ColBG, ColLast }; /* color */
enum { NetSupported, NetWMName, NetWMState,
- NetWMFullscreen, NetActiveWindow, NetLast }; /* EWMH atoms */
+ NetWMFullscreen, NetActiveWindow, NetWMWindowType,
+ NetWMWindowTypeDialog, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
struct Monitor {
char ltsymbol[16];
float mfact;
+ int nmaster;
int num;
int by; /* bar geometry */
int mx, my, mw, mh; /* screen size */
static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, Bool focused);
static void grabkeys(void);
+static void incnmaster(const Arg *arg);
static void initfont(const char *fontstr);
static void keypress(XEvent *e);
static void killclient(const Arg *arg);
static void updatenumlockmask(void);
static void updatesizehints(Client *c);
static void updatestatus(void);
+static void updatewindowtype(Client *c);
static void updatetitle(Client *c);
static void updatewmhints(Client *c);
static void view(const Arg *arg);
unsigned int i;
const Rule *r;
Monitor *m;
- XClassHint ch = { 0 };
+ XClassHint ch = { NULL, NULL };
/* rule matching */
c->isfloating = c->tags = 0;
- if(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;
- }
+ 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);
}
+ 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];
}
*h = bh;
if(*w < bh)
*w = bh;
- if(resizehints || c->isfloating) {
+ if(resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
/* 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 */
showhide(m->stack);
else for(m = mons; m; m = m->next)
showhide(m->stack);
- focus(NULL);
if(m)
arrangemon(m);
else for(m = mons; m; m = m->next)
}
if(ev->window == selmon->barwin) {
i = x = 0;
- do {
+ do
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;
}
else if(ev->x < x + blw)
click = ClkLtSymbol;
- else if(ev->x > selmon->wx + selmon->ww - TEXTW(stext))
+ else if(ev->x > selmon->ww - TEXTW(stext))
click = ClkStatusText;
else
click = ClkWinTitle;
if(!c)
return;
if(cme->message_type == netatom[NetWMState] && cme->data.l[1] == netatom[NetWMFullscreen]) {
- if(cme->data.l[0]) {
+ if(cme->data.l[0] && !c->isfullscreen) {
XChangeProperty(dpy, cme->window, netatom[NetWMState], XA_ATOM, 32,
PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
c->isfullscreen = True;
configurenotify(XEvent *e) {
Monitor *m;
XConfigureEvent *ev = &e->xconfigure;
+ Bool dirty;
if(ev->window == root) {
+ dirty = (sw != ev->width);
sw = ev->width;
sh = ev->height;
- if(updategeom()) {
+ if(updategeom() || dirty) {
if(dc.drawable != 0)
XFreePixmap(dpy, dc.drawable);
dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
updatebars();
for(m = mons; m; m = m->next)
XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
+ focus(NULL);
arrange(NULL);
}
}
c->bw = ev->border_width;
else if(c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
m = c->mon;
- if(ev->value_mask & CWX)
+ if(ev->value_mask & CWX) {
+ c->oldx = c->x;
c->x = m->mx + ev->x;
- if(ev->value_mask & CWY)
+ }
+ if(ev->value_mask & CWY) {
+ c->oldy = c->y;
c->y = m->my + ev->y;
- if(ev->value_mask & CWWidth)
+ }
+ if(ev->value_mask & CWWidth) {
+ c->oldw = c->w;
c->w = ev->width;
- if(ev->value_mask & CWHeight)
+ }
+ 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 - c->w / 2); /* center in x direction */
+ 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 - c->h / 2); /* center in y direction */
+ 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))
die("fatal: could not malloc() %u bytes\n", 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];
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);
- }
+ 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
enternotify(XEvent *e) {
+ Client *c;
Monitor *m;
XCrossingEvent *ev = &e->xcrossing;
if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
return;
- if((m = wintomon(ev->window)) && m != selmon) {
+ c = wintoclient(ev->window);
+ m = c ? c->mon : wintomon(ev->window);
+ if(m != selmon) {
unfocus(selmon->sel, True);
selmon = m;
}
- focus((wintoclient(ev->window)));
+ else if(!c || c == selmon->sel)
+ return;
+ focus(c);
}
void
KeyCode code;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
- for(i = 0; i < LENGTH(keys); i++) {
+ for(i = 0; i < LENGTH(keys); i++)
if((code = XKeysymToKeycode(dpy, keys[i].keysym)))
for(j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
True, GrabModeAsync, GrabModeAsync);
- }
}
}
+void
+incnmaster(const Arg *arg) {
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
void
initfont(const char *fontstr) {
char *def, **missing;
int n;
- missing = NULL;
dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
if(missing) {
while(n--)
applyrules(c);
}
/* geometry */
- c->x = c->oldx = wa->x + c->mon->wx;
- c->y = c->oldy = wa->y + c->mon->wy;
+ 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;
if(c->w == c->mon->mw && c->h == c->mon->mh) {
- c->isfloating = True;
+ c->isfloating = True; // regression with flash, XXXX
c->x = c->mon->mx;
c->y = c->mon->my;
c->bw = 0;
XConfigureWindow(dpy, w, CWBorderWidth, &wc);
XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
configure(c); /* propagates border_width, if size doesn't change */
+ updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
attach(c);
attachstack(c);
XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
- XMapWindow(dpy, c->win);
setclientstate(c, NormalState);
+ if (c->mon == selmon)
+ unfocus(selmon->sel, False);
+ c->mon->sel = c;
arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ focus(NULL);
}
void
if(c == c->mon->sel)
drawbar(c->mon);
}
+ if(ev->atom == netatom[NetWMWindowType])
+ updatewindowtype(c);
}
}
return m;
return selmon;
}
+
void
quit(const Arg *arg) {
running = False;
XEvent ev;
/* main event loop */
XSync(dpy, False);
- while(running && !XNextEvent(dpy, &ev)) {
+ while(running && !XNextEvent(dpy, &ev))
if(handler[ev.type])
handler[ev.type](&ev); /* call handler */
- }
}
void
netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", 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);
/* init cursors */
cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
}
else { /* hide clients bottom up */
showhide(c->snext);
- XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
+ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
}
}
tag(const Arg *arg) {
if(selmon->sel && arg->ui & TAGMASK) {
selmon->sel->tags = arg->ui & TAGMASK;
+ focus(NULL);
arrange(selmon);
}
}
void
tile(Monitor *m) {
- int x, y, h, w, mw;
- unsigned int i, n;
+ 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;
- /* master */
- c = nexttiled(m->clients);
- mw = m->mfact * m->ww;
- resize(c, m->wx, m->wy, (n == 1 ? m->ww : mw) - 2 * c->bw, m->wh - 2 * c->bw, False);
- if(--n == 0)
- return;
- /* tile stack */
- x = (m->wx + mw > c->x + c->w) ? c->x + c->w + 2 * c->bw : m->wx + mw;
- y = m->wy;
- w = (m->wx + mw > c->x + c->w) ? m->wx + m->ww - x : m->ww - mw;
- h = m->wh / n;
- if(h < bh)
- h = m->wh;
- for(i = 0, c = nexttiled(c->next); c; c = nexttiled(c->next), i++) {
- resize(c, x, y, w - 2 * c->bw, /* remainder */ ((i + 1 == n)
- ? m->wy + m->wh - y - 2 * c->bw : h - 2 * c->bw), False);
- if(h != m->wh)
- y = c->y + HEIGHT(c);
- }
+
+ 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), False);
+ 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), False);
+ ty += HEIGHT(c);
+ }
}
void
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if(newtags) {
selmon->sel->tags = newtags;
+ focus(NULL);
arrange(selmon);
}
}
if(newtagset) {
selmon->tagset[selmon->seltags] = newtagset;
+ focus(NULL);
arrange(selmon);
}
}
Client *c;
XUnmapEvent *ev = &e->xunmap;
- if((c = wintoclient(ev->window)))
- unmanage(c, False);
+ if((c = wintoclient(ev->window))) {
+ if(ev->send_event)
+ setclientstate(c, WithdrawnState);
+ else
+ unmanage(c, False);
+ }
}
void
drawbar(selmon);
}
+void
+updatewindowtype(Client *c)
+{
+ Atom wtype, real;
+ int format;
+ unsigned long n, extra;
+ unsigned char *p = NULL;
+
+ if(XGetWindowProperty(dpy, c->win, netatom[NetWMWindowType], 0L,
+ sizeof(Atom), False, XA_ATOM, &real, &format,
+ &n, &extra, (unsigned char **)&p) == Success && p) {
+ wtype = *(Atom *)p;
+ XFree(p);
+
+ if(wtype == netatom[NetWMWindowTypeDialog])
+ c->isfloating = True;
+ }
+}
+
void
updatewmhints(Client *c) {
XWMHints *wmh;
selmon->seltags ^= 1; /* toggle sel tagset */
if(arg->ui & TAGMASK)
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focus(NULL);
arrange(selmon);
}