/* See LICENSE file for copyright and license details. This wm is forked from dwm 6.7 (but keeps up with all dwm's updates), thanks suckless for their incredible work on dwm! Infinite tags module is heavily inspired from 5element which is inspired from the hevel wayland compositor. vxwm 2.2 // by wh1tepearl I just realised that i haven't commenting the entire code, sure i can perfectly read it but for the people that want to fork vxwm/make something with vxwm's code it is a pain in the ass. From this moment, i'll try to comment the code and also make it more readable. */ // Modules configuration is in modules.h // Config is in config.h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XINERAMA #include #endif /* XINERAMA */ #include #include "modules.h" #include "drw.h" #include "util.h" #if INFINITE_TAGS && !WINDOWMAP #undef WINDOWMAP #define WINDOWMAP 1 #endif #if ENHANCED_TOGGLE_FLOATING && !FLOATING_LAYOUT_FLOATS_WINDOWS #undef FLOATING_LAYOUT_FLOATS_WINDOWS #define FLOATING_LAYOUT_FLOATS_WINDOWS 1 #endif /* 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) #if !WINDOWMAP #if !PDWM_LIKE_TAGS_ANIMATION #if !SLOWER_TAGS_ANIMATION #define SHOWHIDEPROFILE XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); // Vanilla #else #define SHOWHIDEPROFILE XMoveWindow(dpy, c->win, WIDTH(c) * -1, c->y); // Slower vanilla #endif #else #if !SLOWER_TAGS_ANIMATION #define SHOWHIDEPROFILE XMoveWindow(dpy, c->win, c->mon->wx + c->mon->ww / 2, -(HEIGHT(c) * 3) / 2); // pdwm vanilla #else #define SHOWHIDEPROFILE XMoveWindow(dpy, c->win, c->mon->wx + c->mon->ww / 2, -(HEIGHT(c))); // Slower pdwm #endif #endif #else #define SHOWHIDEPROFILE if (c->ismapped) { \ window_unmap(dpy, c->win, root, 1); \ c->ismapped = 0; \ } #endif /* This is purely for a bit less sucky config */ #if !XRDB #define MAYBE_CONST const #else #define MAYBE_CONST #endif /* enums */ enum { CurNormal, CurResize, CurMove, #if BETTER_RESIZE && BR_CHANGE_CURSOR CurNW, CurNE, CurSW, CurSE, // corner cursors CurN, CurS, CurE, CurW, // edge cursors #endif CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetWMFullscreen, NetActiveWindow, NetWMWindowType, #if !EWMH_TAGS NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ #else //EWMH_TAGS NetWMWindowTypeDialog, NetClientList, NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, NetDesktopNum, NetLast }; /* EWMH atoms */ #endif 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; #if INFINITE_TAGS int saved_cx, saved_cy; int saved_cw, saved_ch; int was_on_canvas; #endif #if WINDOWMAP int ismapped; #endif #if ENHANCED_TOGGLE_FLOATING int sfx, sfy, sfw, sfh; #if RESTORE_SIZE_AND_POS_ETF int wasmanuallyedited; #endif #endif }; typedef struct { unsigned int mod; KeySym keysym; void (*func)(const Arg *); const Arg arg; } Key; typedef struct { const char *symbol; void (*arrange)(Monitor *); } Layout; #if INFINITE_TAGS typedef struct { int cx, cy; int saved_cx, saved_cy; } CanvasOffset; #endif 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 */ #if GAPS int gappx; /* gaps between windows */ #endif 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]; #if INFINITE_TAGS CanvasOffset *canvas; #endif #if EXTERNAL_BARS int strut_top, strut_bottom, strut_left, strut_right; #endif }; 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); #include "modules/vxwm_includes.h" /* 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 */ #if BAR_PADDING static int vp; /* vertical padding for bar */ static int sp; /* side padding for bar */ #endif 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" #include "modules/vxwm_includes.c" /* 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 WINDOWMAP XGrabServer(dpy); #endif if (m) showhide(m->stack); else for (m = mons; m; m = m->next) showhide(m->stack); #if WINDOWMAP XUngrabServer(dpy); XSync(dpy, False); #endif 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 - 1); m->ltsymbol[sizeof m->ltsymbol - 1] = '\0'; 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) { #if !OCCUPIED_TAGS_DECORATION unsigned int i, x, click; #else unsigned int i, x, click, occ; #endif 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) { #if !OCCUPIED_TAGS_DECORATION i = x = 0; #else i = x = occ = 0; /* Bitmask of occupied tags */ for (c = m->clients; c; c = c->next) occ |= c->tags; #endif do #if !OCCUPIED_TAGS_DECORATION x += TEXTW(tags[i]); #else x += TEXTW(occ & 1 << i ? occupiedtags[i] : tags[i]); #endif 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) + lrpad - 2) 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++) drw_scm_free(drw, scheme[i], 3); 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); #if INFINITE_TAGS free(mon->canvas); #endif 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); #if !BAR_PADDING XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); #else XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); #endif } 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; #if GAPS m->gappx = gappx; #endif m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); #if INFINITE_TAGS m->canvas = ecalloc(LENGTH(tags), sizeof(CanvasOffset)); unsigned int i; for (i = 0; i < LENGTH(tags); i++) { m->canvas[i].cx = 0; m->canvas[i].cy = 0; } #endif return m; } void destroynotify(XEvent *e) { Client *c; XDestroyWindowEvent *ev = &e->xdestroywindow; if ((c = wintoclient(ev->window))) unmanage(c, 1); #if EXTERNAL_BARS externalbars_unregister(ev->window); #endif } 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; #if OCCUPIED_TAGS_DECORATION const char *tagtext; #endif 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 */ #if !BAR_PADDING drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); #else drw_text(drw, m->ww - tw - 2 * sp, 0, tw, bh, 0, stext, 0); #endif } 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++) { #if !OCCUPIED_TAGS_DECORATION 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); #else tagtext = occ & 1 << i ? occupiedtags[i] : tags[i]; w = TEXTW(tagtext); drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, tagtext, urg & 1 << i); #endif 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 INFINITE_TAGS && IT_SHOW_COORDINATES_IN_BAR #if COORDINATES_DIVISOR <= 0 #undef COORDINATES_DIVISOR #define COORDINATES_DIVISOR 1 #endif if (selmon->lt[selmon->sellt]->arrange == NULL) { int tagidx = getcurrenttag(m); char coords[64]; snprintf(coords, sizeof(coords), "[x%d y%d]", m->canvas[tagidx].cx / COORDINATES_DIVISOR, m->canvas[tagidx].cy / COORDINATES_DIVISOR); w = TEXTW(coords); drw_setscheme(drw, scheme[SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, coords, 0); x += w; } #endif if ((w = m->ww - tw - x) > bh) { if (m->sel) { #if !ALT_CENTER_OF_BAR_COLOR drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); #else drw_setscheme(drw, scheme[m == selmon ? SchemeNorm : SchemeNorm]); #endif #if !BAR_PADDING drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); #else drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0); #endif if (m->sel->isfloating) drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); } else { drw_setscheme(drw, scheme[SchemeNorm]); #if !BAR_PADDING drw_rect(drw, x, 0, w, bh, 1, 1); #else drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); #endif } } 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); #if INFINITE_TAGS centerwindow(NULL); #endif #if WARP_TO_CLIENT && WARP_TO_CENTER_OF_WINDOW_AFFECTED_BY_FOCUSSTACK warptoclient(c); #endif restack(selmon); } } Atom getatomprop(Client *c, Atom prop) { int format; unsigned long nitems, dl; unsigned char *p = NULL; Atom da, atom = None; if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, &da, &format, &nitems, &dl, &p) == Success && p) { if (nitems > 0 && format == 32) atom = *(long *)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, &p) != Success) return -1; if (n != 0 && format == 32) result = *(long *)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); } } #if WARP_TO_CLIENT && WARP_TO_CENTER_OF_WINDOW_AFFECTED_BY_INCNMASTER // LARP_TO_CLIENT void incnmaster(const Arg *arg) { Client *c; unsigned int n; Client *moved = NULL; for (n = 0, c = nexttiled(selmon->clients); c; c = nexttiled(c->next), n++); if (arg->i > 0) { moved = nexttiled(selmon->clients); for (n = 0; moved && n < selmon->nmaster; n++) moved = nexttiled(moved->next); } else if (arg->i < 0 && selmon->nmaster > 0) { moved = nexttiled(selmon->clients); for (n = 1; moved && n < selmon->nmaster; n++) moved = nexttiled(moved->next); } selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); arrange(selmon); if (moved) warptoclient(moved); } #else void incnmaster(const Arg *arg) { selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); arrange(selmon); } #endif #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; #if WINDOWMAP c->ismapped = 0; #endif /* 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; #if ENHANCED_TOGGLE_FLOATING c->sfx = c->x; c->sfy = c->y; c->sfw = c->w; c->sfh = c->h; #endif 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 FLOATING_LAYOUT_FLOATS_WINDOWS if (selmon->lt[selmon->sellt]->arrange == NULL) c->isfloating = 1; #endif 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); #if CENTER_NEW_FLOATING_WINDOWS && !NEW_WINDOWS_APPEAR_UNDER_CURSOR c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; #endif #if NEW_FLOATING_WINDOWS_APPEAR_UNDER_CURSOR int mx, my, di; unsigned int dui; Window dw; XQueryPointer(dpy, root, &dw, &dw, &mx, &my, &di, &di, &dui); c->x = mx - c->w / 2; c->y = my - c->h / 2; #endif 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); #if EWMH_TAGS updatewmdesktop(c); #endif 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); #if WARP_TO_CLIENT && WARP_TO_CENTER_OF_NEW_WINDOW warptoclient(c); #endif } 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 EXTERNAL_BARS if (externalbars_hasstrut(ev->window)) { externalbars_register(ev->window); XMapWindow(dpy, ev->window); XSelectInput(dpy, ev->window, PropertyChangeMask|StructureNotifyMask); return; } #endif 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; #if LOCK_MOVE_RESIZE_REFRESH_RATE Time lasttime = 0; #endif //LOCK_MOVE_RESIZE_REFRESH_RATE 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 LOCK_MOVE_RESIZE_REFRESH_RATE if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) continue; lasttime = ev.xmotion.time; #endif //LOCK_MOVE_RESIZE_REFRESH_RATE 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 !MOVE_IN_TILED if (!c->isfloating && selmon->lt[selmon->sellt]->arrange && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) togglefloating(NULL); #endif if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) resize(c, nx, ny, c->w, c->h, 1); #if MOVE_IN_TILED else if (selmon->lt[selmon->sellt]->arrange || !c->isfloating) { if ((m = recttomon(ev.xmotion.x_root, ev.xmotion.y_root, 1, 1)) != selmon) { sendmon(c, m); selmon = m; focus(NULL); } Client *cc = c->mon->clients; while (1) { if (cc == 0) break; if( cc != c && !cc->isfloating && ISVISIBLE(cc) && ev.xmotion.x_root > cc->x && ev.xmotion.x_root < cc->x + cc->w && ev.xmotion.y_root > cc->y && ev.xmotion.y_root < cc->y + cc->h ) { break; } cc = cc->next; } #if !INFINITE_TAGS if (cc) { Client *cl1, *cl2, ocl1; if (!selmon->lt[selmon->sellt]->arrange) return; cl1 = c; cl2 = cc; ocl1 = *cl1; strcpy(cl1->name, cl2->name); cl1->win = cl2->win; cl1->x = cl2->x; cl1->y = cl2->y; cl1->w = cl2->w; cl1->h = cl2->h; cl2->win = ocl1.win; strcpy(cl2->name, ocl1.name); cl2->x = ocl1.x; cl2->y = ocl1.y; cl2->w = ocl1.w; cl2->h = ocl1.h; selmon->sel = cl2; c = cc; focus(c); arrange(cl1->mon); } #else // TODO: make this an option in modules.h if (cc) { Client *cl1, *cl2, ocl1; if (!selmon->lt[selmon->sellt]->arrange) return; cl1 = c; cl2 = cc; ocl1 = *cl1; strcpy(cl1->name, cl2->name); cl1->win = cl2->win; cl1->x = cl2->x; cl1->y = cl2->y; cl1->w = cl2->w; cl1->h = cl2->h; cl2->win = ocl1.win; strcpy(cl2->name, ocl1.name); cl2->x = ocl1.x; cl2->y = ocl1.y; cl2->w = ocl1.w; cl2->h = ocl1.h; int tmp_cx = cl1->saved_cx; int tmp_cy = cl1->saved_cy; int tmp_cw = cl1->saved_cw; int tmp_ch = cl1->saved_ch; int tmp_was = cl1->was_on_canvas; cl1->saved_cx = cl2->saved_cx; cl1->saved_cy = cl2->saved_cy; cl1->saved_cw = cl2->saved_cw; cl1->saved_ch = cl2->saved_ch; cl1->was_on_canvas = cl2->was_on_canvas; cl2->saved_cx = tmp_cx; cl2->saved_cy = tmp_cy; cl2->saved_cw = tmp_cw; cl2->saved_ch = tmp_ch; cl2->was_on_canvas = tmp_was; selmon->sel = cl2; c = cc; focus(c); arrange(cl1->mon); } #endif } #endif 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); } #if ENHANCED_TOGGLE_FLOATING && RESTORE_SIZE_AND_POS_ETF c->wasmanuallyedited = 1; if (c->isfloating) { c->sfx = c->x; c->sfy = c->y; c->sfw = c->w; c->sfh = c->h; } #endif } 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 EXTERNAL_BARS if (ev->atom == XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False) || ev->atom == XInternAtom(dpy, "_NET_WM_STRUT", False)) { if (ev->state == PropertyNewValue) externalbars_register(ev->window); else externalbars_unregister(ev->window); return; } #endif 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); } #if !BETTER_RESIZE void resizemouse(const Arg *arg) { int ocx, ocy, nw, nh; Client *c; Monitor *m; XEvent ev; #if LOCK_MOVE_RESIZE_REFRESH_RATE Time lasttime = 0; #endif 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 LOCK_MOVE_RESIZE_REFRESH_RATE if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) continue; lasttime = ev.xmotion.time; #endif 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 !RESIZING_WINDOWS_IN_ALL_LAYOUTS_FLOATS_THEM if (!c->isfloating && selmon->lt[selmon->sellt]->arrange && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) togglefloating(NULL); #else if (!c->isfloating && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) togglefloating(NULL); #endif } if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) #if USE_RESIZECLIENT_FUNC resizeclient(c, c->x, c->y, nw, nh); #else resize(c, c->x, c->y, nw, nh, 1); #endif 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); } #if ENHANCED_TOGGLE_FLOATING && RESTORE_SIZE_AND_POS_ETF c->wasmanuallyedited = 1; if (c->isfloating) { c->sfx = c->x; c->sfy = c->y; c->sfw = c->w; c->sfh = c->h; } #endif } #endif //BETTER_RESIZE 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; } } #if INFINITE_TAGS else { if (m->sel && m->sel->isfullscreen) return; wc.stack_mode = Below; wc.sibling = m->barwin; for (c = m->stack; c; c = c->snext) { if (ISVISIBLE(c) && !c->isfixed && !c->isfullscreen) { XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); wc.sibling = c->win; } } XRaiseWindow(dpy, m->barwin); } #endif 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 EXTERNAL_BARS externalbars_begin_scan(); #endif 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) { #if EXTERNAL_BARS if (externalbars_hasstrut(wins[i])) { externalbars_register(wins[i]); continue; } #endif 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); } #if EXTERNAL_BARS externalbars_end_scan(); #endif } 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); #if EWMH_TAGS updatewmdesktop(c); #endif if (c->isfullscreen) resizeclient(c, m->mx, m->my, m->mw, m->mh); 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 INFINITE_TAGS const Layout *temp_new_layout = (arg && arg->v) ? (Layout *)arg->v : selmon->lt[selmon->sellt ^ 1]; if (temp_new_layout == selmon->lt[selmon->sellt]) return; const Layout *old_layout = selmon->lt[selmon->sellt]; #endif if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; #if INFINITE_TAGS const Layout *new_layout = selmon->lt[selmon->sellt]; if (old_layout->arrange == NULL && new_layout->arrange != NULL) { save_canvas_positions(selmon); homecanvas(NULL); Client *c; for (c = selmon->clients; c; c = c->next) if (!c->isfixed) c->isfloating = 0; } if (new_layout->arrange == NULL) { restore_canvas_positions(selmon); Client *c; for (c = selmon->clients; c; c = c->next) c->isfloating = 1; } #endif strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol - 1); selmon->ltsymbol[sizeof selmon->ltsymbol - 1] = '\0'; arrange(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 */ //this comment got me giggling so hard man, lol 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; #if !BAR_HEIGHT bh = drw->fonts->h + 2; #else bh = user_bh ? user_bh : drw->fonts->h + 2; #endif updategeom(); #if BAR_PADDING sp = sidepad; vp = (topbar == 1) ? vertpad : - vertpad; #endif /* 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); #if EWMH_TAGS netatom[NetDesktopViewport] = XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); netatom[NetNumberOfDesktops] = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); netatom[NetCurrentDesktop] = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); netatom[NetDesktopNames] = XInternAtom(dpy, "_NET_DESKTOP_NAMES", False); netatom[NetDesktopNum] = XInternAtom(dpy, "_NET_WM_DESKTOP", False); #endif /* 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); #if BETTER_RESIZE && BR_CHANGE_CURSOR cursor[CurNW] = drw_cur_create(drw, XC_top_left_corner); cursor[CurNE] = drw_cur_create(drw, XC_top_right_corner); cursor[CurSW] = drw_cur_create(drw, XC_bottom_left_corner); cursor[CurSE] = drw_cur_create(drw, XC_bottom_right_corner); cursor[CurN] = drw_cur_create(drw, XC_top_side); cursor[CurS] = drw_cur_create(drw, XC_bottom_side); cursor[CurE] = drw_cur_create(drw, XC_right_side); cursor[CurW] = drw_cur_create(drw, XC_left_side); #endif /* 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(); #if BAR_PADDING updatebarpos(selmon); #endif /* 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 *) "vxwm", 4); 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); #if EWMH_TAGS setnumdesktops(); setcurrentdesktop(); setdesktopnames(); setviewport(); #endif 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 */ #if !WINDOWMAP 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); #else if (!c->ismapped) { window_map(dpy, c, 1); c->ismapped = 1; } #endif showhide(c->snext); } else { /* hide clients bottom up */ showhide(c->snext); SHOWHIDEPROFILE } } 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("vxwm: execvp '%s' failed:", ((char **)arg->v)[0]); } } void tag(const Arg *arg) { if (selmon->sel && arg->ui & TAGMASK) { #if INFINITE_TAGS Client *c = selmon->sel; unsigned int target_tag_mask = arg->ui & TAGMASK; int i; for (i = 0; i < LENGTH(tags); i++) { if (target_tag_mask & (1 << i)) { c->saved_cx = selmon->canvas[i].cx + (selmon->ww - WIDTH(c)) / 2; c->saved_cy = selmon->canvas[i].cy + (selmon->wh - HEIGHT(c)) / 2; c->saved_cw = c->w; c->saved_ch = c->h; c->was_on_canvas = 1; break; } } #endif selmon->sel->tags = arg->ui & TAGMASK; #if EWMH_TAGS updatewmdesktop(selmon->sel); #endif 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 #if !GAPS 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 //GAPS 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) - m->gappx; resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); if (my + HEIGHT(c) + m->gappx < m->wh) my += HEIGHT(c) + m->gappx; #endif //GAPS } else { #if !GAPS 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); #else //GAPS 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); if (ty + HEIGHT(c) + m->gappx < m->wh) ty += HEIGHT(c) + m->gappx; #endif //GAPS } } void togglebar(const Arg *arg) { selmon->showbar = !selmon->showbar; updatebarpos(selmon); int bar_y; if (selmon->showbar) { bar_y = selmon->by; } else { if (topbar) { bar_y = -bh; } else { bar_y = selmon->mh + bh; } } #if !BAR_PADDING XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, bar_y, selmon->ww, bh); #else int final_y = (selmon->showbar) ? (bar_y + vp) : bar_y; XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, final_y, selmon->ww - 2 * sp, bh); #endif 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; #if EWMH_TAGS updatewmdesktop(selmon->sel); #endif focus(NULL); arrange(selmon); } #if EWMH_TAGS updatecurrentdesktop(); #endif } 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); } #if EWMH_TAGS updatecurrentdesktop(); #endif } 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); #if WARP_TO_CLIENT && WARP_TO_CENTER_OF_PREVIOUS_WINDOW if (m == selmon && m->sel) warptoclient(m->sel); #endif } 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); } #if EXTERNAL_BARS externalbars_unregister(ev->window); #endif } void updatebars(void) { Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, .background_pixmap = ParentRelative, .event_mask = ButtonPressMask|ExposureMask }; XClassHint ch = {"vxwm", "vxwm"}; for (m = mons; m; m = m->next) { if (m->barwin) continue; #if !BAR_PADDING m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), #else m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen), #endif 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) { #if !BAR_PADDING m->wh -= bh; m->by = m->topbar ? m->wy : m->wy + m->wh; m->wy = m->topbar ? m->wy + bh : m->wy; #else m->wh = m->wh - vertpad - bh; m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; m->wy = m->topbar ? m->wy + bh + vp : m->wy; #endif } else #if !BAR_PADDING m->by = -bh; #else m->by = -bh - vp; #endif #if EXTERNAL_BARS m->wx += m->strut_left; m->ww -= m->strut_left + m->strut_right; m->wy += m->strut_top; m->wh -= m->strut_top + m->strut_bottom; if (m->ww < 1) m->ww = 1; if (m->wh < 1) m->wh = 1; #endif } 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, "vxwm-"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); } } #if TAG_TO_TAG void view(const Arg *arg) { #if INFINITE_TAGS if (selmon->lt[selmon->sellt]->arrange == NULL) { save_canvas_positions(selmon); } #endif if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) selmon->seltags ^= 1; else { selmon->seltags ^= 1; if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; } #if INFINITE_TAGS int newtag = getcurrenttag(selmon); if (selmon->lt[selmon->sellt]->arrange == NULL) { restore_canvas_positions(selmon); Client *c; for (c = selmon->clients; c; c = c->next) if (c->tags & (1 << newtag)) c->isfloating = 1; } else { selmon->canvas[newtag].cx = 0; selmon->canvas[newtag].cy = 0; } #endif focus(NULL); arrange(selmon); #if EWMH_TAGS updatecurrentdesktop(); #endif } #else void view(const Arg *arg) { #if INFINITE_TAGS if (selmon->lt[selmon->sellt]->arrange == NULL) save_canvas_positions(selmon); #endif if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; #if INFINITE_TAGS int newtag = getcurrenttag(selmon); if (selmon->lt[selmon->sellt]->arrange != NULL) { selmon->canvas[newtag].cx = 0; selmon->canvas[newtag].cy = 0; } else { restore_canvas_positions(selmon); Client *c; for (c = selmon->clients; c; c = c->next) if (ISVISIBLE(c)) c->isfloating = 1; } #endif focus(NULL); arrange(selmon); #if EWMH_TAGS updatecurrentdesktop(); #endif } #endif 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, "vxwm: 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("vxwm: another window manager is already running"); return -1; } void zoom(const Arg *arg) { Client *c = selmon->sel; #if WARP_TO_CLIENT && WARP_TO_CENTER_OF_ZOOMED_WINDOW Client *target = c; #endif if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) return; if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) return; pop(c); #if WARP_TO_CLIENT && WARP_TO_CENTER_OF_ZOOMED_WINDOW warptoclient(target); #endif } int main(int argc, char *argv[]) { if (argc == 2 && !strcmp("-v", argv[1])) die("vxwm "VERSION); if (argc == 2 && !strcmp("-srcdir", argv[1])) die(SRCDIR); if (argc == 2 && !strcmp("-ignoreautostart", argv[1])) printf("Ignoring autostart"); else if (argc != 1) die("usage: vxwm [-v] [-srcdir]" #if AUTOSTART " [-ignoreautostart]" #endif ); if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fputs("warning: no locale support\n", stderr); if (!(dpy = XOpenDisplay(NULL))) die("vxwm: cannot open display"); checkotherwm(); #if XRDB XrmInitialize(); loadxrdb(); #endif setup(); #ifdef __OpenBSD__ if (pledge("stdio rpath proc exec", NULL) == -1) die("pledge"); #endif /* __OpenBSD__ */ scan(); #if AUTOSTART if (!(argc == 2 && !strcmp("-ignoreautostart", argv[1]))) runautostart(); #endif run(); cleanup(); XCloseDisplay(dpy); return EXIT_SUCCESS; }