/* See LICENSE file for copyright and license details. * * dynamic window manager is designed like any other X client as well. It is * driven through handling X events. In contrast to other X clients, a window * manager selects for SubstructureRedirectMask on the root window, to receive * events about window (dis-)appearance. Only one X connection at a time is * allowed to select for this event mask. * * The event handlers of dwm are organized in an array which is accessed * whenever a new event has been fetched. This allows event dispatching * in O(1) time. * * Each child of the root window is called a client, except windows which have * set the override_redirect flag. Clients are organized in a linked client * list on each monitor, the focus history is remembered through a stack list * on each monitor. Each client contains a bit array to indicate the tags of a * client. * * Keys and tagging rules are organized as arrays and defined in config.h. * * To understand everything else, start reading main(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XINERAMA #include #endif /* XINERAMA */ #include #include #include #include #include #include #include "drw.h" #include "util.h" /* macros */ #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) #define 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) #define TAGMASK ((1 << LENGTH(tags)) - 1) #define TEXTW(X) (drw_font_getwidth(drw, (X), False) + lrpad) #define TEXTWM(X) (drw_font_getwidth(drw, (X), True) + lrpad) /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel, SchemeTabActive, SchemeTabInactive }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ typedef union { int i; unsigned int ui; float f; const void *v; } Arg; typedef struct { unsigned int click; unsigned int mask; unsigned int button; void (*func)(const Arg *arg); const Arg arg; } Button; typedef struct Monitor Monitor; typedef struct Client Client; struct Client { char name[256]; float mina, maxa; int x, y, w, h; int oldx, oldy, oldw, oldh; int basew, baseh, incw, inch, maxw, maxh, minw, minh; int bw, oldbw; unsigned int tags; int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow, iskbd; pid_t pid; Client *next; Client *snext; Client *swallowing; Monitor *mon; Window win; }; typedef struct { int type; unsigned int mod; KeySym keysym; void (*func)(const Arg *); const Arg arg; } Key; typedef struct { const char *symbol; void (*arrange)(Monitor *); } Layout; struct HoldKey { KeySym keysym; struct HoldKey *next; }; struct Monitor { char ltsymbol[16]; float mfact; int nmaster; int num; int by; /* bar geometry */ int mx, my, mw, mh; /* screen size */ int wx, wy, ww, wh; /* window area */ unsigned int seltags; unsigned int sellt; unsigned int tagset[2]; int showbar; int topbar; Client *clients; Client *sel; Client *stack; Monitor *next; Window barwin; Window softwin; const Layout *lt[2]; }; typedef struct { const char *class; const char *instance; const char *title; unsigned int tags; int isfloating; int isterminal; int noswallow; int monitor; int iskbd; } 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 attachBelow(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 cyclelayout(const Arg *arg); static void deck(Monitor *m); static void deckdouble(Monitor *m); 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 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 inplacerotate(const Arg *arg); static void keypress(XEvent *e); static void keyrelease(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 *); 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 sigchld(int unused); static void spawn(const Arg *arg); static void switchcol(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *); static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void transfer(const Arg *arg); static void transferall(const Arg *arg); static void unfloatvisible(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); static void bstack(Monitor *m); static void bstackhoriz(Monitor *m); static void clienttagpush(const Arg *arg); static void shiftview(const Arg *arg); static void pushup(const Arg *arg); static void pushdown(const Arg *arg); static void readxresources(void); static void reload_dwm_theme(void); static void load_plugins(void); static void unload_plugins(void); static pid_t getparentprocess(pid_t p); static int isdescprocess(pid_t p, pid_t c); static Client *swallowingclient(Window w); static Client *termforwin(const Client *c); static pid_t winpid(Window w); /* variables */ static const char broken[] = "broken"; static char stext[512]; static int screen; static int sw, sh; /* X display screen geometry width, height */ static int bh, blw = 0; /* bar geometry */ static int lrpad; /* sum of left and right padding for text */ static int (*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, [KeyRelease] = keyrelease, [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; /* Empty arrays to be filled from command line, Xresources, and defaults, in decreasing order of precedence */ static char *colors[4][3]; /* 4 schemes, 3 colors each */ static char active_font[256]; static xcb_connection_t *xcon; static struct HoldKey *holdkeyroot = NULL; /* configuration, allows nested code to access above variables */ #include "config.h" /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; /* function implementations */ void applyrules(Client *c) { const char *class, *instance; unsigned int i; const Rule *r; Monitor *m; XClassHint ch = { NULL, NULL }; /* rule matching */ c->isfloating = 0; c->tags = 0; XGetClassHint(dpy, c->win, &ch); class = ch.res_class ? ch.res_class : broken; instance = ch.res_name ? ch.res_name : broken; for (i = 0; i < LENGTH(rules); i++) { r = &rules[i]; if ((!r->title || strstr(c->name, r->title)) && (!r->class || strstr(class, r->class)) && (!r->instance || strstr(instance, r->instance))) { c->isterminal = r->isterminal; c->isfloating = r->isfloating; c->iskbd = r->iskbd; 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) { /* 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 bartabdraw(Monitor *m, Client *c, int unused, int x, int w, int groupactive) { if (!c) return; int i, nclienttags = 0, nviewtags = 0; drw_setscheme(drw, scheme[ m->sel == c ? SchemeSel : (groupactive ? SchemeTabActive: SchemeTabInactive) ]); drw_text(drw, x, 0, w, bh, lrpad / 2, c->name, 0, False); // pjh just a guess False // Floating win indicator if (c->isfloating) drw_rect(drw, x + 2, 2, 5, 5, 0, 0); // Optional borders between tabs if (BARTAB_BORDERS) { XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBorder].pixel); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, 0, 1, bh); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x + w, 0, 1, bh); } // Optional tags icons for (i = 0; i < LENGTH(tags); i++) { if ((m->tagset[m->seltags] >> i) & 1) { nviewtags++; } if ((c->tags >> i) & 1) { nclienttags++; } } if (BARTAB_TAGSINDICATOR == 2 || nclienttags > 1 || nviewtags > 1) { for (i = 0; i < LENGTH(tags); i++) { drw_rect(drw, ( x + w - 2 - ((LENGTH(tags) / BARTAB_TAGSROWS) * BARTAB_TAGSPX) - (i % (LENGTH(tags)/BARTAB_TAGSROWS)) + ((i % (LENGTH(tags) / BARTAB_TAGSROWS)) * BARTAB_TAGSPX) ), ( 2 + ((i / (LENGTH(tags)/BARTAB_TAGSROWS)) * BARTAB_TAGSPX) - ((i / (LENGTH(tags)/BARTAB_TAGSROWS))) ), BARTAB_TAGSPX, BARTAB_TAGSPX, (c->tags >> i) & 1, 0 ); } } } void battabclick(Monitor *m, Client *c, int passx, int x, int w, int unused) { if (passx >= x && passx <= x + w) { focus(c); restack(selmon); } } void bartabcalculate( Monitor *m, int offx, int sw, int passx, void(*tabfn)(Monitor *, Client *, int, int, int, int) ) { Client *c; int i, clientsnmaster = 0, clientsnstack = 0, clientsnfloating = 0, masteractive = 0, fulllayout = 0, floatlayout = 0, x, w, tgactive; for (i = 0, c = m->clients; c; c = c->next) { if (!ISVISIBLE(c)) continue; if (c->isfloating) { clientsnfloating++; continue; } if (m->sel == c) { masteractive = i < m->nmaster; } if (i < m->nmaster) { clientsnmaster++; } else { clientsnstack++; } i++; } for (i = 0; i < LENGTH(bartabfloatfns); i++) if (m ->lt[m->sellt]->arrange == bartabfloatfns[i]) { floatlayout = 1; break; } for (i = 0; i < LENGTH(bartabmonfns); i++) if (m ->lt[m->sellt]->arrange == bartabmonfns[i]) { fulllayout = 1; break; } for (c = m->clients, i = 0; c; c = c->next) { if (!ISVISIBLE(c)) continue; if (clientsnmaster + clientsnstack == 0 || floatlayout) { x = offx + (((m->mw - offx - sw) / (clientsnmaster + clientsnstack + clientsnfloating)) * i); w = (m->mw - offx - sw) / (clientsnmaster + clientsnstack + clientsnfloating); tgactive = 1; } else if (!c->isfloating && (fulllayout || ((clientsnmaster == 0) ^ (clientsnstack == 0)))) { x = offx + (((m->mw - offx - sw) / (clientsnmaster + clientsnstack)) * i); w = (m->mw - offx - sw) / (clientsnmaster + clientsnstack); tgactive = 1; } else if (i < m->nmaster && !c->isfloating) { x = offx + ((((m->mw * m->mfact) - offx) /clientsnmaster) * i); w = ((m->mw * m->mfact) - offx) / clientsnmaster; tgactive = masteractive; } else if (!c->isfloating) { x = (m->mw * m->mfact) + ((((m->mw * (1 - m->mfact)) - sw) / clientsnstack) * (i - m->nmaster)); w = ((m->mw * (1 - m->mfact)) - sw) / clientsnstack; tgactive = !masteractive; } else continue; tabfn(m, c, passx, x, w, tgactive); i++; } } void arrange(Monitor *m) { if (m) showhide(m->stack); else for (m = mons; m; m = m->next) showhide(m->stack); if (m) { arrangemon(m); restack(m); } else for (m = mons; m; m = m->next) arrangemon(m); } void arrangemon(Monitor *m) { strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); } void attach(Client *c) { c->next = c->mon->clients; c->mon->clients = c; } void attachBelow(Client *c) { //If there is nothing on the monitor or the selected client is floating, attach as normal if(c->mon->sel == NULL || c->mon->sel->isfloating) { attach(c); return; } //Set the new client's next property to the same as the currently selected clients next c->next = c->mon->sel->next; //Set the currently selected clients next property to the new client c->mon->sel->next = c; } void attachstack(Client *c) { c->snext = c->mon->stack; c->mon->stack = c; } void swallow(Client *p, Client *c) { if (c->noswallow || c->isterminal) return; detach(c); detachstack(c); setclientstate(c, WithdrawnState); XUnmapWindow(dpy, p->win); p->swallowing = c; c->mon = p->mon; Window w = p->win; p->win = c->win; c->win = w; updatetitle(p); arrange(p->mon); XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); configure(p); updateclientlist(); } void unswallow(Client *c) { c->win = c->swallowing->win; free(c->swallowing); c->swallowing = NULL; updatetitle(c); arrange(c->mon); XMapWindow(dpy, c->win); XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); configure(c); setclientstate(c, NormalState); } void buttonpress(XEvent *e) { unsigned int i, x, click; Arg arg = {0}; Client *c; Monitor *m; XButtonPressedEvent *ev = &e->xbutton; click = ClkRootWin; /* focus monitor if necessary */ if (!((c = wintoclient(ev->window)) && c->iskbd) && (m = wintomon(ev->window)) && m != selmon) { unfocus(selmon->sel, 1); selmon = m; focus(NULL); } if (ev->window == selmon->barwin) { i = x = 0; 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)); if (i < LENGTH(tags)) { click = ClkTagBar; arg.ui = 1 << i; } else if (ev->x < x + blw) click = ClkLtSymbol; else if (ev->x > selmon->ww - TEXTWM(stext)) click = ClkStatusText; else // Focus clicked tab bar item bartabcalculate(selmon, x, TEXTW(stext) - lrpad + 2, ev->x, battabclick); } else if ((c = wintoclient(ev->window))) { if (!c->iskbd) { focus(c); restack(selmon); } XAllowEvents(dpy, ReplayPointer, CurrentTime); click = ClkClientWin; } for (i = 0; i < LENGTH(buttons); i++) if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); } void checkotherwm(void) { xerrorxlib = XSetErrorHandler(xerrorstart); /* this causes an error if some other window manager is running */ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); XSync(dpy, False); XSetErrorHandler(xerror); XSync(dpy, False); } void cleanup(void) { Arg a = {.ui = ~0}; Layout foo = { "", NULL }; Monitor *m; size_t i; view(&a); selmon->lt[selmon->sellt] = &foo; for (m = mons; m; m = m->next) while (m->stack) unmanage(m->stack, 0); XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); for (i = 0; i < CurLast; i++) drw_cur_free(drw, cursor[i]); for (i = 0; i < LENGTH(colors); i++) free(scheme[i]); unload_plugins(); XDestroyWindow(dpy, wmcheckwin); drw_free(drw); XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); } void deck(Monitor *m) { unsigned int i, n, h, mw, my; 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; snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n - m->nmaster); } else mw = m->ww; for(i = my = 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 resize(c, m->wx + mw, m->wy, m->ww - mw - (2*c->bw), m->wh - (2*c->bw), False); } void deckdouble(Monitor *m) { unsigned int i, n, h, mw, ns; 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; ns = m->nmaster > 0 ? 2 : 1; } else { mw = m->ww; ns = 1; } for(i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) if(i < m->nmaster) resize(c, m->wx, m->wy, mw - (2*c->bw), m->wh - (2*c->bw), False); else resize(c, m->wx + mw, m->wy, m->ww - mw - (2*c->bw), m->wh - (2*c->bw), False); } 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 (mon->softwin) { XUnmapWindow(dpy, mon->softwin); XDestroyWindow(dpy, mon->softwin); } 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 */)); } 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; 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) { XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); } focus(NULL); arrange(NULL); } } } void configurerequest(XEvent *e) { Client *c; Monitor *m; XConfigureRequestEvent *ev = &e->xconfigurerequest; XWindowChanges wc; if ((c = wintoclient(ev->window))) { if (ev->value_mask & CWBorderWidth) c->bw = ev->border_width; else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { m = c->mon; if (ev->value_mask & CWX) { c->oldx = c->x; c->x = m->mx + ev->x; } if (ev->value_mask & CWY) { c->oldy = c->y; c->y = m->my + ev->y; } if (ev->value_mask & CWWidth) { c->oldw = c->w; c->w = ev->width; } if (ev->value_mask & CWHeight) { c->oldh = c->h; c->h = ev->height; } if ((c->x + c->w) > m->mx + m->mw && c->isfloating) c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ if ((c->y + c->h) > m->my + m->mh && c->isfloating) c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) configure(c); if (ISVISIBLE(c)) XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); } else configure(c); } else { wc.x = ev->x; wc.y = ev->y; wc.width = ev->width; wc.height = ev->height; wc.border_width = ev->border_width; wc.sibling = ev->above; wc.stack_mode = ev->detail; XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); } XSync(dpy, False); } Monitor * createmon(void) { Monitor *m; m = ecalloc(1, sizeof(Monitor)); m->tagset[0] = m->tagset[1] = 1; m->mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; m->topbar = topbar; m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); return m; } void cyclelayout(const Arg *arg) { int i; Arg a; for(i = 0; i < LENGTH(ppcyclelayouts) && &ppcyclelayouts[i] != selmon->lt[selmon->sellt]; i++); if(arg->i > 0) { a.v = &ppcyclelayouts[i + 1 < LENGTH(ppcyclelayouts) ? i + 1 : 0]; setlayout(&a); } } void destroynotify(XEvent *e) { Client *c; XDestroyWindowEvent *ev = &e->xdestroywindow; if ((c = wintoclient(ev->window))) unmanage(c, 1); else if ((c = swallowingclient(ev->window))) unmanage(c->swallowing, 1); else focus(NULL); } 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, sw = 0; unsigned int i, occ = 0, urg = 0; Client *c; drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, 0, 0, m->ww, bh, 1, 1); for (c = m->clients; c; c = c->next) { if (!c->iskbd) { occ |= c->tags; if (c->isurgent) urg |= c->tags; } } 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, False); x += w; } w = blw = TEXTW(m->ltsymbol); drw_setscheme(drw, scheme[SchemeNorm]); x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0, False); if (m == selmon) { /* status is drawn on selected monitor */ const char *s = stext; drw_setscheme(drw, scheme[SchemeNorm]); while (*s == ' ') s++; sw = TEXTWM(s) - lrpad; while (*s && sw > m->ww - x) { const char *next = strchr(s, ' '); if (!next) break; while (*next == ' ') next++; if (!*next) break; s = next; sw = TEXTWM(s) - lrpad; } drw_text(drw, MAX(x, m->ww - sw), 0, sw, bh, 0, s, 0, True); } drw_map(drw, m->barwin, 0, 0, m->ww, bh); if (m->softwin && drw && drw->font) { XLowerWindow(dpy, m->softwin); XClearWindow(dpy, m->softwin); if (!m->sel) { XftDraw *d = XftDrawCreate(dpy, m->softwin, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); if (d) { unsigned int ew = 0, eh = 0; const char *left_txt = "Menu"; const char *right_txt = "Names"; int sh_bar = 26; drw_font_getexts(drw->font, left_txt, strlen(left_txt), &ew, &eh, False); int ty = (sh_bar - (int)eh) / 2; pango_layout_set_text(drw->font->layout, left_txt, strlen(left_txt)); pango_xft_render_layout(d, &scheme[SchemeSel][ColFg], drw->font->layout, 6 * PANGO_SCALE, ty * PANGO_SCALE); drw_font_getexts(drw->font, right_txt, strlen(right_txt), &ew, &eh, False); ty = (sh_bar - (int)eh) / 2; int rx = m->ww - (int)ew - 6; pango_layout_set_text(drw->font->layout, right_txt, strlen(right_txt)); pango_xft_render_layout(d, &scheme[SchemeSel][ColFg], drw->font->layout, rx * PANGO_SCALE, ty * PANGO_SCALE); XftDrawDestroy(d); } } } } 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); } static void write_sysfs_str(const char *path, const char *val) { int fd = open(path, O_WRONLY | O_CLOEXEC); if (fd >= 0) { (void)write(fd, val, strlen(val)); close(fd); } } static void read_sysfs_str(const char *path, char *buf, size_t len) { int fd = open(path, O_RDONLY | O_CLOEXEC); ssize_t n; if (fd >= 0) { n = read(fd, buf, len - 1); close(fd); if (n > 0) { while (n > 0 && (buf[n - 1] == '\n' || buf[n - 1] == '\r')) n--; buf[n] = '\0'; return; } } buf[0] = '\0'; } static void set_hw_backlight(int on) { static char saved_brightness[16] = "14"; char cur[16] = ""; if (on) { read_sysfs_str("/tmp/sxmo_saved_brightness", cur, sizeof(cur)); if (atoi(cur) > 1) snprintf(saved_brightness, sizeof(saved_brightness), "%s", cur); else if (atoi(saved_brightness) <= 1) { read_sysfs_str("/sys/class/backlight/backlight/max_brightness", cur, sizeof(cur)); snprintf(saved_brightness, sizeof(saved_brightness), "%s", (atoi(cur) > 1) ? cur : "14"); } write_sysfs_str("/sys/class/backlight/backlight/bl_power", "0"); write_sysfs_str("/sys/class/backlight/backlight/brightness", saved_brightness); write_sysfs_str("/sys/class/leds/white:kbd_backlight/brightness", "255"); } else { read_sysfs_str("/sys/class/backlight/backlight/brightness", cur, sizeof(cur)); if (atoi(cur) > 1) { snprintf(saved_brightness, sizeof(saved_brightness), "%s", cur); int fd = open("/tmp/sxmo_saved_brightness", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (fd >= 0) { (void)write(fd, saved_brightness, strlen(saved_brightness)); close(fd); } } write_sysfs_str("/sys/class/leds/white:kbd_backlight/brightness", "0"); write_sysfs_str("/sys/class/backlight/backlight/brightness", "0"); } } static int is_phone_locked(void) { const char *rdir = getenv("XDG_RUNTIME_DIR"); char path[256], buf[32]; int fd; ssize_t n; snprintf(path, sizeof(path), "%s/sxmo.state", (rdir && rdir[0]) ? rdir : "/run/user/10000"); fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) return 0; n = read(fd, buf, sizeof(buf) - 1); close(fd); if (n <= 0) return 0; buf[n] = '\0'; return (!strncmp(buf, "screenoff", 9) || !strncmp(buf, "lock", 4)); } static void writefocusedwin(Client *c) { const char *rdir = getenv("XDG_RUNTIME_DIR"); char path[256], cls[256] = "", ttl[256] = ""; XClassHint ch = { NULL, NULL }; FILE *fp; size_t i; snprintf(path, sizeof(path), "%s/sxmo.focusedwin", (rdir && rdir[0]) ? rdir : "/run/user/10000"); if (c) { if (XGetClassHint(dpy, c->win, &ch)) { if (ch.res_class) { snprintf(cls, sizeof(cls), "%s", ch.res_class); XFree(ch.res_class); } if (ch.res_name) XFree(ch.res_name); } snprintf(ttl, sizeof(ttl), "%s", c->name); for (i = 0; cls[i]; i++) cls[i] = (char)tolower((unsigned char)cls[i]); for (i = 0; ttl[i]; i++) ttl[i] = (char)tolower((unsigned char)ttl[i]); } if ((fp = fopen(path, "w"))) { fprintf(fp, "%s\n%s\n", cls, ttl); fclose(fp); } } static int lidfd = -1; void focus(Client *c) { if (!c || !ISVISIBLE(c)) for (c = selmon->stack; c && (!ISVISIBLE(c) || c->iskbd) ; 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; writefocusedwin(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) return; if (arg->i > 0) { for (c = selmon->sel->next; c && (!ISVISIBLE(c) || c->iskbd); c = c->next); if (!c) for (c = selmon->clients; c && (!ISVISIBLE(c) || c->iskbd); c = c->next); } else { for (i = selmon->clients; i != selmon->sel; i = i->next) if (ISVISIBLE(i) && !i->iskbd) c = i; if (!c) for (; i; i = i->next) if (ISVISIBLE(i) && !i->iskbd) c = i; } if (c) { focus(c); restack(selmon); } } Atom getatomprop(Client *c, Atom prop) { int di; unsigned long dl; unsigned char *p = NULL; Atom da, atom = None; if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; XFree(p); } return atom; } int getrootptr(int *x, int *y) { int di; unsigned int dui; Window dummy; return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); } long getstate(Window w) { int format; long result = -1; unsigned char *p = NULL; unsigned long n, extra; Atom real; if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], &real, &format, &n, &extra, (unsigned char **)&p) != Success) return -1; if (n != 0) result = *p; XFree(p); return result; } int gettextprop(Window w, Atom atom, char *text, unsigned int size) { char **list = NULL; int n; XTextProperty name; if (!text || size == 0) return 0; text[0] = '\0'; if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) return 0; if (name.encoding == XA_STRING) strncpy(text, (char *)name.value, size - 1); else { if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { strncpy(text, *list, size - 1); XFreeStringList(list); } } text[size - 1] = '\0'; XFree(name.value); return 1; } void grabbuttons(Client *c, int focused) { updatenumlockmask(); { unsigned int i, j; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; XUngrabButton(dpy, AnyButton, AnyModifier, c->win); if (!focused) XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, GrabModeSync, GrabModeSync, None, None); for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].click == ClkClientWin) for (j = 0; j < LENGTH(modifiers); j++) XGrabButton(dpy, buttons[i].button, buttons[i].mask | modifiers[j], c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } } void grabkeys(void) { updatenumlockmask(); { unsigned int i, j, k; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; int start, end, skip; KeySym *syms; XUngrabKey(dpy, AnyKey, AnyModifier, root); XDisplayKeycodes(dpy, &start, &end); syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); if (!syms) return; for (k = start; k <= end; k++) for (i = 0; i < LENGTH(keys); i++) /* skip modifier codes, we do that ourselves */ if (keys[i].keysym == syms[(k - start) * skip]) for (j = 0; j < LENGTH(modifiers); j++) XGrabKey(dpy, k, keys[i].mod | modifiers[j], root, True, GrabModeAsync, GrabModeAsync); XFree(syms); } } void incnmaster(const Arg *arg) { selmon->nmaster = MAX(selmon->nmaster + arg->i, 1); arrange(selmon); } #ifdef XINERAMA static int isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) { while (n--) if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org && unique[n].width == info->width && unique[n].height == info->height) return 0; return 1; } #endif /* XINERAMA */ int iswide() { return sw > 1000 ? 1 : 0; } void markhold(KeySym keysym) { struct HoldKey *new = malloc(sizeof(struct HoldKey)); new->keysym = keysym; new->next = NULL; if (holdkeyroot == NULL) { holdkeyroot = new; return; } struct HoldKey *cur = holdkeyroot; while (cur->next != NULL) cur = cur->next; cur->next = new; } void markreleased(KeySym keysym) { if (holdkeyroot == NULL) { return; } struct HoldKey *cur = holdkeyroot; if (cur->keysym == keysym) { holdkeyroot = cur->next; free(cur); return; } while (cur->next != NULL) { if (cur->next->keysym == keysym) { struct HoldKey *toremove = cur->next; cur->next = toremove->next; free(toremove); return; } cur = cur->next; } } int ishold(KeySym keysym) { struct HoldKey *cur = holdkeyroot; while (cur != NULL) { if (cur->keysym == keysym) return 1; cur = cur->next; }; return 0; } #define SOFTKEY_LEFT 1 #define SOFTKEY_RIGHT 2 #define SOFTKEY_CHORD_MS 30 #define SOFTKEY_CANCEL_WINDOW_MS 320 #define SOFTKEY_SUPPRESS_MS 500 static int softkey_pending = 0; static long long softkey_first_ms = 0; static long long softkey_deadline_ms = 0; static long long softkey_suppress_until_ms = 0; static int last_softkey_mask = 0; static long long last_softkey_ms = 0; static pid_t last_menu_pgid = 0; static long long dwm_now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (long long)ts.tv_sec * 1000LL + (long long)(ts.tv_nsec / 1000000L); } static void write_softkey_suppress(long long until_ms) { char buf[32]; int fd, len; softkey_suppress_until_ms = until_ms; fd = open("/tmp/sxmo_softkey_suppress", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (fd >= 0) { len = snprintf(buf, sizeof(buf), "%lld\n", until_ms); (void)write(fd, buf, len); close(fd); } } static void write_softkey_state(int mask, long long ms) { char buf[64]; int fd = open("/tmp/sxmo_softkey_state", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (fd >= 0) { int len = snprintf(buf, sizeof(buf), "%d %lld\n", mask, ms); (void)write(fd, buf, len); close(fd); } } static long long read_softkey_suppress(void) { char buf[32]; long long now = dwm_now_ms(); if (softkey_suppress_until_ms > now + 5000LL) softkey_suppress_until_ms = 0; read_sysfs_str("/tmp/sxmo_softkey_suppress", buf, sizeof(buf)); long long v = atoll(buf); if (v > now + 5000LL) { unlink("/tmp/sxmo_softkey_suppress"); v = 0; } if (v > softkey_suppress_until_ms) softkey_suppress_until_ms = v; return softkey_suppress_until_ms; } static int query_softkeys_down(void) { char km[32]; int mask = 0; if (!XQueryKeymap(dpy, km)) return 0; if (km[135 / 8] & (1 << (135 % 8))) mask |= SOFTKEY_LEFT; if (km[166 / 8] & (1 << (166 % 8))) mask |= SOFTKEY_RIGHT; return mask; } static int is_left_softkey(KeySym ks, unsigned int keycode) { return ks == XK_Menu || ks == XF86XK_KbdLcdMenu1 || keycode == 135; } static int is_right_softkey(KeySym ks, unsigned int keycode) { return ks == XF86XK_Back || ks == XF86XK_KbdLcdMenu2 || keycode == 166; } #include #include #include "dwm_plugin.h" #define MAX_DWM_PLUGINS 16 typedef struct { void *handle; const DwmPlugin *plugin; } LoadedDwmPlugin; static LoadedDwmPlugin loaded_plugins[MAX_DWM_PLUGINS]; static int num_loaded_plugins = 0; static int host_is_idle_desktop(void) { return (selmon && selmon->sel == NULL && !is_phone_locked()) ? 1 : 0; } static int host_is_phone_locked(void) { return is_phone_locked(); } static void host_spawn_command(const char *const *argv) { if (!argv || !argv[0]) return; Arg a = { .v = (const void *)argv }; XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); spawn(&a); } static int is_plugin_daemon_alive(const char *plugin_name) { char pidpath[128], buf[256]; snprintf(pidpath, sizeof(pidpath), "/tmp/dwm_plugin_%s.pid", plugin_name); read_sysfs_str(pidpath, buf, sizeof(buf)); int pid = atoi(buf); if (pid <= 0 || kill(pid, 0) != 0) return 0; snprintf(pidpath, sizeof(pidpath), "/proc/%d/cmdline", pid); int fd = open(pidpath, O_RDONLY | O_CLOEXEC); if (fd < 0) return 0; ssize_t n = read(fd, buf, sizeof(buf) - 1); close(fd); if (n <= 0) return 0; for (ssize_t i = 0; i < n; i++) { if (buf[i] == '\0') buf[i] = ' '; } buf[n] = '\0'; return (strstr(buf, "dwm-plugin") != NULL && strstr(buf, plugin_name) != NULL); } static int preload_stagger_idx = 0; static void host_spawn_plugin(const char *plugin_name, const char *arg) { if (!plugin_name || !plugin_name[0]) return; if (arg && strcmp(arg, "--preload") == 0) { if (is_plugin_daemon_alive(plugin_name)) return; int stagger_ms = (strcmp(plugin_name, "dmenu") == 0) ? 150 : (600 + (preload_stagger_idx++ * 220)); if (fork() == 0) { if (dpy) close(ConnectionNumber(dpy)); setsid(); usleep((useconds_t)stagger_ms * 1000U); execlp("dwm-plugin", "dwm-plugin", plugin_name, "--preload", (char *)NULL); _exit(EXIT_SUCCESS); } return; } XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); if (is_plugin_daemon_alive(plugin_name)) { char fifopath[128], msg[128]; snprintf(fifopath, sizeof(fifopath), "/tmp/dwm_plugin_%s.fifo", plugin_name); int fd = open(fifopath, O_WRONLY | O_NONBLOCK | O_CLOEXEC); if (fd >= 0) { int len; if (strcmp(plugin_name, "xdrawer") == 0) len = snprintf(msg, sizeof(msg), "TOGGLE\n"); else if (strcmp(plugin_name, "dmenu") == 0 && arg && strcmp(arg, "close") == 0) len = snprintf(msg, sizeof(msg), "CLOSE\n"); else if (arg && arg[0]) len = snprintf(msg, sizeof(msg), "OPEN %s\n", arg); else len = snprintf(msg, sizeof(msg), "OPEN\n"); if (write(fd, msg, len) == len) { close(fd); return; } close(fd); } } const char *cmd[4]; cmd[0] = "dwm-plugin"; cmd[1] = plugin_name; cmd[2] = (arg && arg[0]) ? arg : NULL; cmd[3] = NULL; Arg a = { .v = cmd }; spawn(&a); } static DwmHostApi dwm_host_api = { .api_version = DWM_PLUGIN_API_VERSION, .dpy = NULL, .root = 0, .screen = 0, .sw = 0, .sh = 0, .bh = 0, .is_idle_desktop = host_is_idle_desktop, .is_phone_locked = host_is_phone_locked, .spawn_command = host_spawn_command, .spawn_plugin = host_spawn_plugin, }; static int is_plugin_loaded(const char *name) { int i; for (i = 0; i < num_loaded_plugins; i++) { if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->name && strcmp(loaded_plugins[i].plugin->name, name) == 0) return 1; } return 0; } static void load_plugins_from_dir(const char *dirpath) { DIR *dir; struct dirent *ent; char path[512]; if (!dirpath || !dirpath[0]) return; dir = opendir(dirpath); if (!dir) return; while ((ent = readdir(dir)) != NULL && num_loaded_plugins < MAX_DWM_PLUGINS) { size_t len = strlen(ent->d_name); if (len <= 3 || strcmp(ent->d_name + len - 3, ".so") != 0) continue; snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name); void *h = dlopen(path, RTLD_NOW | RTLD_LOCAL); if (!h) continue; const DwmPlugin *plug = (const DwmPlugin *)dlsym(h, DWM_PLUGIN_ENTRY_SYMBOL); if (!plug || plug->api_version != DWM_PLUGIN_API_VERSION || !plug->name || is_plugin_loaded(plug->name)) { dlclose(h); continue; } if (plug->init && plug->init(&dwm_host_api) != 0) { dlclose(h); continue; } loaded_plugins[num_loaded_plugins].handle = h; loaded_plugins[num_loaded_plugins].plugin = plug; num_loaded_plugins++; } closedir(dir); } static void load_plugins(void) { dwm_host_api.dpy = dpy; dwm_host_api.root = root; dwm_host_api.screen = screen; dwm_host_api.sw = sw; dwm_host_api.sh = sh; dwm_host_api.bh = bh; const char *env_dir = getenv("DWM_PLUGIN_PATH"); if (env_dir && env_dir[0]) load_plugins_from_dir(env_dir); load_plugins_from_dir("/home/user/.local/lib/dwm/plugins"); load_plugins_from_dir("/usr/lib/dwm/plugins"); } static void unload_plugins(void) { int i; for (i = 0; i < num_loaded_plugins; i++) { if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->cleanup) loaded_plugins[i].plugin->cleanup(); if (loaded_plugins[i].handle) dlclose(loaded_plugins[i].handle); } num_loaded_plugins = 0; } static int dispatch_plugin_softkey_chord(void) { int i; for (i = 0; i < num_loaded_plugins; i++) { if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->on_softkey_chord) { if (loaded_plugins[i].plugin->on_softkey_chord(&dwm_host_api)) return 1; } } return 0; } static int dispatch_plugin_idle_key(KeySym keysym, unsigned int keycode, unsigned int state) { int i; for (i = 0; i < num_loaded_plugins; i++) { if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->on_idle_key) { if (loaded_plugins[i].plugin->on_idle_key(&dwm_host_api, keysym, keycode, state)) return 1; } } return 0; } static int dispatch_plugin_global_key(KeySym keysym, unsigned int keycode, unsigned int state, int is_press) { int i; for (i = 0; i < num_loaded_plugins; i++) { if (loaded_plugins[i].plugin && loaded_plugins[i].plugin->on_global_key) { if (loaded_plugins[i].plugin->on_global_key(&dwm_host_api, keysym, keycode, state, is_press)) return 1; } } return 0; } static pid_t spawn_pgid(const Arg *arg) { pid_t pid = fork(); if (pid == 0) { if (dpy) close(ConnectionNumber(dpy)); setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); _exit(EXIT_SUCCESS); } return pid; } static void trigger_softkey_chord(long long now) { if (last_menu_pgid > 0) { kill(-last_menu_pgid, SIGKILL); last_menu_pgid = 0; } if (is_plugin_loaded("dmenu")) host_spawn_plugin("dmenu", "close"); softkey_pending = 0; softkey_first_ms = 0; softkey_deadline_ms = 0; last_softkey_mask = 0; last_softkey_ms = 0; write_softkey_state(0, 0); write_softkey_suppress(now + SOFTKEY_SUPPRESS_MS); XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); if (!dispatch_plugin_softkey_chord()) { const Arg xdrawercmd = { .v = (const char*[]){ "xdrawer", NULL } }; spawn(&xdrawercmd); } } static void flush_pending_softkey(void) { int pending = softkey_pending; softkey_pending = 0; softkey_first_ms = 0; softkey_deadline_ms = 0; XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); if (pending == SOFTKEY_LEFT) { if (is_plugin_loaded("dmenu")) { host_spawn_plugin("dmenu", "sys"); return; } const Arg syscmd = { .v = (const char*[]){ "sxmo_appmenu.sh", "sys", NULL } }; last_menu_pgid = spawn_pgid(&syscmd); } else if (pending == SOFTKEY_RIGHT) { if (host_is_idle_desktop() && dispatch_plugin_idle_key(XF86XK_Back, 166, 0)) { return; } if (is_plugin_loaded("dmenu")) { host_spawn_plugin("dmenu", "ctx"); return; } const Arg ctxcmd = { .v = (const char*[]){ "sxmo_appmenu.sh", NULL } }; last_menu_pgid = spawn_pgid(&ctxcmd); } } void keypress(XEvent *e) { unsigned int i; KeySym keysym; XKeyEvent *ev; ev = &e->xkey; keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); if ((keysym == XF86XK_PowerOff || keysym == XF86XK_PickupPhone || keysym == XF86XK_Phone || keysym == XK_Menu || keysym == XF86XK_Back || keysym == XK_Return || keysym == XF86XK_KbdLcdMenu1 || keysym == XF86XK_KbdLcdMenu2) && is_phone_locked()) { const Arg unlockcmd = SHCMD("xset dpms 0 0 0; xset dpms force on; sxmo_state.sh set unlock"); softkey_pending = 0; softkey_first_ms = 0; softkey_deadline_ms = 0; last_softkey_mask = 0; last_softkey_ms = 0; write_softkey_state(0, 0); write_softkey_suppress(dwm_now_ms() + SOFTKEY_SUPPRESS_MS); XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); set_hw_backlight(1); spawn(&unlockcmd); return; } if (CLEANMASK(ev->state) == 0 && (is_left_softkey(keysym, ev->keycode) || is_right_softkey(keysym, ev->keycode))) { long long now = dwm_now_ms(); if (now < read_softkey_suppress()) { XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); return; } int which = 0; if (is_left_softkey(keysym, ev->keycode)) which |= SOFTKEY_LEFT; if (is_right_softkey(keysym, ev->keycode)) which |= SOFTKEY_RIGHT; int combined = softkey_pending | which | query_softkeys_down(); if (last_softkey_mask != 0 && (now - last_softkey_ms) <= SOFTKEY_CANCEL_WINDOW_MS) combined |= last_softkey_mask; if (combined == (SOFTKEY_LEFT | SOFTKEY_RIGHT)) { trigger_softkey_chord(now); } else { softkey_pending = which; last_softkey_mask = which; last_softkey_ms = now; write_softkey_state(which, now); if (softkey_deadline_ms == 0) { softkey_first_ms = now; softkey_deadline_ms = now + SOFTKEY_CHORD_MS; } } return; } if (!is_phone_locked() && dispatch_plugin_global_key(keysym, ev->keycode, CLEANMASK(ev->state), 1)) { return; } if (!selmon->sel && !is_phone_locked()) { if (dispatch_plugin_idle_key(keysym, ev->keycode, CLEANMASK(ev->state))) return; } if (!selmon->sel && CLEANMASK(ev->state) == 0) { if (keysym == XK_Return) { XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); if (is_plugin_loaded("dmenu")) { host_spawn_plugin("dmenu", "sys"); return; } const Arg entercmd = { .v = (const char*[]){ "sxmo_appmenu.sh", NULL } }; spawn(&entercmd); return; } else if (keysym == XK_Left) { const Arg prevtag = { .i = -1 }; shiftview(&prevtag); return; } else if (keysym == XK_Right) { const Arg nexttag = { .i = +1 }; shiftview(&nexttag); return; } } for (i = 0; i < LENGTH(keys); i++) { if (keysym == keys[i].keysym && KeyPress == keys[i].type && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].func) { XUngrabKeyboard(dpy, CurrentTime); XFlush(dpy); keys[i].func(&(keys[i].arg)); } } } void keyrelease(XEvent *e) { unsigned int i; KeySym keysym; XKeyEvent *ev; ev = &e->xkey; keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); markreleased(keysym); if (is_left_softkey(keysym, ev->keycode) || is_right_softkey(keysym, ev->keycode)) { return; } for (i = 0; i < LENGTH(keys); i++) { if (keysym == keys[i].keysym && KeyRelease == keys[i].type && 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, *term = NULL; Window trans = None; XWindowChanges wc; c = ecalloc(1, sizeof(Client)); c->win = w; c->pid = winpid(w); /* geometry */ c->x = c->oldx = wa->x; c->y = c->oldy = wa->y; c->w = c->oldw = wa->width; c->h = c->oldh = wa->height; c->oldbw = wa->border_width; if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { c->mon = t->mon; c->tags = t->tags; } else { c->mon = selmon; applyrules(c); term = termforwin(c); } if (c->iskbd) { c->y = c->mon->my + c->mon->mh - c->h; c->mon->mh = c->mon->mh - HEIGHT(c); updatebarpos(selmon); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); arrange(selmon); } else { updatetitle(c); if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) c->x = c->mon->mx + c->mon->mw - WIDTH(c); if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) c->y = c->mon->my + c->mon->mh - HEIGHT(c); c->x = MAX(c->x, c->mon->mx); /* only fix client y-offset, if the client center might cover the bar */ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); } c->bw = borderpx; wc.border_width = c->bw; XConfigureWindow(dpy, w, CWBorderWidth, &wc); XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); configure(c); /* propagates border_width, if size doesn't change */ updatewindowtype(c); updatesizehints(c); updatewmhints(c); XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); grabbuttons(c, 0); if (!c->isfloating) c->isfloating = c->oldstate = t || c->isfixed; if (c->isfloating) XRaiseWindow(dpy, c->win); if( attachbelow ) attachBelow(c); else attach(c); attachstack(c); XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ setclientstate(c, NormalState); if (!c->iskbd) { if (c->mon == selmon) unfocus(selmon->sel, 0); c->mon->sel = c; } arrange(c->mon); XMapWindow(dpy, c->win); if (term) swallow(term, c); focus(NULL); } void mappingnotify(XEvent *e) { XMappingEvent *ev = &e->xmapping; XRefreshKeyboardMapping(ev); if (ev->request == MappingKeyboard) grabkeys(); } void maprequest(XEvent *e) { static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; if (!XGetWindowAttributes(dpy, ev->window, &wa)) return; if (wa.override_redirect) return; if (!wintoclient(ev->window)) manage(ev->window, &wa); } void monocle(Monitor *m) { unsigned int n = 0; Client *c; for (c = m->clients; c; c = c->next) if (ISVISIBLE(c) && !c->iskbd) n++; if (n > 0) /* override layout symbol */ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); } void motionnotify(XEvent *e) { static Monitor *mon = NULL; Monitor *m; XMotionEvent *ev = &e->xmotion; if (ev->window != root) return; if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { unfocus(selmon->sel, 1); selmon = m; focus(NULL); } mon = m; } void movemouse(const Arg *arg) { int x, y, ocx, ocy, nx, ny; Client *c; Monitor *m; XEvent ev; Time lasttime = 0; if (!(c = selmon->sel)) return; restack(selmon); ocx = c->x; ocy = c->y; if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) return; if (!getrootptr(&x, &y)) return; do { XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); switch(ev.type) { case ConfigureRequest: case Expose: case MapRequest: handler[ev.type](&ev); break; case MotionNotify: if ((ev.xmotion.time - lasttime) <= (1000 / 60)) continue; lasttime = ev.xmotion.time; nx = ocx + (ev.xmotion.x - x); ny = ocy + (ev.xmotion.y - y); if (abs(selmon->wx - nx) < snap) nx = selmon->wx; else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) nx = selmon->wx + selmon->ww - WIDTH(c); if (abs(selmon->wy - ny) < snap) ny = selmon->wy; else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) ny = selmon->wy + selmon->wh - HEIGHT(c); //if (!c->isfloating && selmon->lt[selmon->sellt]->arrange //&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) // togglefloating(NULL); if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) resize(c, nx, ny, c->w, c->h, 1); break; } } while (ev.type != ButtonRelease); XUngrabPointer(dpy, CurrentTime); if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { sendmon(c, m); selmon = m; focus(NULL); } } Client * nexttiled(Client *c) { for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); return c; } void pop(Client *c) { detach(c); attach(c); focus(c); arrange(c->mon); } void propertynotify(XEvent *e) { Client *c; Window trans; XPropertyEvent *ev = &e->xproperty; if (ev->window == root) { if (ev->atom == XA_WM_NAME) updatestatus(); else if (ev->atom == XA_RESOURCE_MANAGER) reload_dwm_theme(); else drawbars(); } 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: updatesizehints(c); 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 (c && c->iskbd) return; if (applysizehints(c, &x, &y, &w, &h, interact)) resizeclient(c, x, y, w, h); } void resizeclient(Client *c, int x, int y, int w, int h) { XWindowChanges wc; c->oldx = c->x; c->x = wc.x = x; c->oldy = c->y; c->y = wc.y = y; c->oldw = c->w; c->w = wc.width = w; c->oldh = c->h; c->h = wc.height = h; wc.border_width = c->bw; XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); configure(c); XSync(dpy, False); } void resizemouse(const Arg *arg) { int ocx, ocy, nw, nh; Client *c; Monitor *m; XEvent ev; Time lasttime = 0; if (!(c = selmon->sel)) return; restack(selmon); ocx = c->x; ocy = c->y; if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) return; if (c->isfloating || NULL == c->mon->lt[c->mon->sellt]->arrange) { XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); } else { XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->mx + (selmon->ww * selmon->mfact), selmon->my + (selmon->wh / 2) ); } do { XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); switch(ev.type) { case ConfigureRequest: case Expose: case MapRequest: handler[ev.type](&ev); break; case MotionNotify: if ((ev.xmotion.time - lasttime) <= (1000 / 60)) continue; lasttime = ev.xmotion.time; nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) resize(c, c->x, c->y, nw, nh, 1); break; } } while (ev.type != ButtonRelease); if (c->isfloating || NULL == c->mon->lt[c->mon->sellt]->arrange) { XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); } else { selmon->mfact = (double) (ev.xmotion.x_root - selmon->mx) / (double) selmon->ww; arrange(selmon); XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->mx + (selmon->ww * selmon->mfact), selmon->my + (selmon->wh / 2) ); } XUngrabPointer(dpy, CurrentTime); while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { sendmon(c, m); selmon = m; focus(NULL); } } void restack(Monitor *m) { Client *c; XEvent ev; XWindowChanges wc; XRaiseWindow(dpy, m->barwin); drawbar(m); if (!m->sel) return; if (m->sel->isfloating || !m->lt[m->sellt]->arrange) XRaiseWindow(dpy, m->sel->win); if (m->lt[m->sellt]->arrange) { wc.stack_mode = Below; wc.sibling = m->barwin; for (c = m->stack; c; c = c->snext) if (!c->isfloating && ISVISIBLE(c)) { XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); wc.sibling = c->win; } } XSync(dpy, False); while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } void run(void) { XEvent ev; struct pollfd fds[2]; struct input_event ie; int nfds, xfd; /* main event loop */ XSync(dpy, False); xfd = ConnectionNumber(dpy); while (running) { while (XPending(dpy)) { if (XNextEvent(dpy, &ev)) return; if (handler[ev.type]) handler[ev.type](&ev); } if (!running) break; fds[0].fd = xfd; fds[0].events = POLLIN; fds[0].revents = 0; nfds = 1; if (lidfd >= 0) { fds[1].fd = lidfd; fds[1].events = POLLIN; fds[1].revents = 0; nfds = 2; } int timeout_ms = -1; if (softkey_pending && softkey_deadline_ms > 0) { long long now = dwm_now_ms(); int hw_down = query_softkeys_down(); softkey_pending |= hw_down; if (softkey_pending == (SOFTKEY_LEFT | SOFTKEY_RIGHT)) { trigger_softkey_chord(now); continue; } long long rem = softkey_deadline_ms - now; if (rem <= 0) { flush_pending_softkey(); continue; } else { timeout_ms = (int)rem; } } if (poll(fds, nfds, timeout_ms) < 0) { if (errno == EINTR) continue; break; } if (nfds == 2 && (fds[1].revents & POLLIN)) { while (read(lidfd, &ie, sizeof(ie)) == (ssize_t)sizeof(ie)) { if (ie.type == EV_SW && ie.code == SW_LID) { if (ie.value == 1) { const Arg offcmd = SHCMD("sxmo_dmenu.sh close; xset dpms 0 0 3; xset dpms force off; sxmo_state.sh set screenoff"); set_hw_backlight(0); spawn(&offcmd); } else if (ie.value == 0) { const Arg oncmd = SHCMD("xset dpms 0 0 0; xset dpms force on; sxmo_state.sh set unlock"); set_hw_backlight(1); spawn(&oncmd); } } } } if (fds[0].revents & (POLLIN | POLLHUP | POLLERR)) { while (XPending(dpy)) { if (XNextEvent(dpy, &ev)) return; if (handler[ev.type]) handler[ev.type](&ev); } } } } void scan(void) { unsigned int i, num; Window d1, d2, *wins = NULL; XWindowAttributes wa; if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { for (i = 0; i < num; i++) { if (!XGetWindowAttributes(dpy, wins[i], &wa) || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) continue; if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) manage(wins[i], &wa); } for (i = 0; i < num; i++) { /* now the transients */ if (!XGetWindowAttributes(dpy, wins[i], &wa)) continue; if (XGetTransientForHint(dpy, wins[i], &d1) && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) manage(wins[i], &wa); } if (wins) XFree(wins); } } void sendmon(Client *c, Monitor *m) { if (c->mon == m) return; unfocus(c, 1); detach(c); detachstack(c); c->mon = m; c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ if( attachbelow ) attachBelow(c); else attach(c); attachstack(c); focus(NULL); arrange(NULL); } void setclientstate(Client *c, long state) { long data[] = { state, None }; XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, PropModeReplace, (unsigned char *)data, 2); } int sendevent(Client *c, Atom proto) { int n; Atom *protocols; int exists = 0; XEvent ev; if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { while (!exists && n--) exists = protocols[n] == proto; XFree(protocols); } if (exists) { ev.type = ClientMessage; ev.xclient.window = c->win; ev.xclient.message_type = wmatom[WMProtocols]; ev.xclient.format = 32; ev.xclient.data.l[0] = proto; ev.xclient.data.l[1] = CurrentTime; XSendEvent(dpy, c->win, False, NoEventMask, &ev); } return exists; } void setfocus(Client *c) { if (!c->neverfocus) { XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); } sendevent(c, wmatom[WMTakeFocus]); } void setfullscreen(Client *c, int fullscreen) { if (fullscreen && !c->isfullscreen) { XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); c->isfullscreen = 1; } else if (!fullscreen && c->isfullscreen){ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, (unsigned char*)0, 0); c->isfullscreen = 0; } } void setlayout(const Arg *arg) { if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); if (selmon->sel) arrange(selmon); else drawbar(selmon); } /* arg > 1.0 will set mfact absolutely */ void setmfact(const Arg *arg) { float f; if (!arg || !selmon->lt[selmon->sellt]->arrange) return; f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.1 || f > 0.9) return; selmon->mfact = f; arrange(selmon); } void setup(void) { int i,j; XSetWindowAttributes wa; Atom utf8string; /* clean up any zombies immediately */ sigchld(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); snprintf(active_font, sizeof(active_font), "%s", font); readxresources(); /* Apply defaults to font and colors*/ for (i = 0; i <= SchemeTabInactive; ++i){ for (j = 0; j <= ColBorder; ++j){ if ( !colors[i][j] ) colors[i][j] = strdup(defaultcolors[i][j]); } } if (!drw_font_create(drw, active_font)) die("no fonts could be loaded."); lrpad = 6; bh = MAX(drw->font->h + 4, 24); updategeom(); /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); /* init cursors */ cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); cursor[CurResize] = drw_cur_create(drw, XC_sizing); cursor[CurMove] = drw_cur_create(drw, XC_fleur); /* init appearance */ scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); for (i = 0; i < LENGTH(colors); i++) scheme[i] = drw_scm_create(drw, (const char **)colors[i], 3); for (j = 0; j <= SchemeTabInactive; ++j) { free(colors[j][ColFg]); free(colors[j][ColBg]); free(colors[j][ColBorder]); colors[j][ColFg] = NULL; colors[j][ColBg] = NULL; colors[j][ColBorder] = NULL; } /* init bars */ updatebars(); updatestatus(); /* supporting window for NetWMCheck */ wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, PropModeReplace, (unsigned char *) &wmcheckwin, 1); XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, PropModeReplace, (unsigned char *) "dwm", 3); XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, PropModeReplace, (unsigned char *) &wmcheckwin, 1); /* EWMH support per view */ XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, PropModeReplace, (unsigned char *) netatom, NetLast); XDeleteProperty(dpy, root, netatom[NetClientList]); /* select events */ wa.cursor = cursor[CurNormal]->cursor; wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask |ButtonPressMask|PointerMotionMask|EnterWindowMask |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask|KeyPressMask|KeyReleaseMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); lidfd = open("/dev/input/by-path/platform-gpio-keys-event", O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (lidfd < 0) lidfd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK | O_CLOEXEC); unlink("/tmp/sxmo_softkey_suppress"); unlink("/tmp/sxmo_softkey_state"); load_plugins(); grabkeys(); focus(NULL); } void seturgent(Client *c, int urg) { XWMHints *wmh; c->isurgent = urg; if (!(wmh = XGetWMHints(dpy, c->win))) return; wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); XSetWMHints(dpy, c->win, wmh); XFree(wmh); } void showhide(Client *c) { if (!c) return; if (ISVISIBLE(c)) { /* show clients top down */ XMoveWindow(dpy, c->win, c->x, c->y); if (!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) resize(c, c->x, c->y, c->w, c->h, 0); showhide(c->snext); } else { /* hide clients bottom up */ showhide(c->snext); XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); } } void sigchld(int unused) { if (signal(SIGCHLD, sigchld) == SIG_ERR) die("can't install SIGCHLD handler:"); while (0 < waitpid(-1, NULL, WNOHANG)); } void spawn(const Arg *arg) { if (arg->v == dmenucmd) dmenumon[0] = '0' + selmon->num; if (fork() == 0) { if (dpy) close(ConnectionNumber(dpy)); setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); perror(" failed"); exit(EXIT_SUCCESS); } } void switchcol(const Arg *arg) { Client *c, *t; int col = 0; int i; if (!selmon->sel) return; for (i = 0, c = nexttiled(selmon->clients); c ; c = nexttiled(c->next), i++) { if (c == selmon->sel) col = (i + 1) > selmon->nmaster; } if (i <= selmon->nmaster) return; for (c = selmon->stack; c; c = c->snext) { if (!ISVISIBLE(c)) continue; for (i = 0, t = nexttiled(selmon->clients); t && t != c; t = nexttiled(t->next), i++); if (t && (i + 1 > selmon->nmaster) != col) { focus(c); restack(selmon); break; } } } void tag(const Arg *arg) { if (selmon->sel && arg->ui & TAGMASK) { selmon->sel->tags = arg->ui & TAGMASK; focus(NULL); arrange(selmon); } } void tagmon(const Arg *arg) { if (!selmon->sel || !mons->next) return; sendmon(selmon->sel, dirtomon(arg->i)); } void tile(Monitor *m) { unsigned int i, n, h, mw, my, ty; Client *c; for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if (n == 0) return; if (n > m->nmaster) mw = m->nmaster ? m->ww * m->mfact : 0; else mw = m->ww; for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) if (i < m->nmaster) { h = (m->wh - my) / (MIN(n, m->nmaster) - i); resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); my += HEIGHT(c); } else { h = (m->wh - ty) / (n - i); resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); ty += HEIGHT(c); } } void togglebar(const Arg *arg) { selmon->showbar = !selmon->showbar; updatebarpos(selmon); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); arrange(selmon); } void togglefloating(const Arg *arg) { if (!selmon->sel) return; selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; if (selmon->sel->isfloating) resize(selmon->sel, selmon->sel->x, selmon->sel->y, selmon->sel->w, selmon->sel->h, 0); arrange(selmon); } void toggletag(const Arg *arg) { unsigned int newtags; if (!selmon->sel) return; newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); if (newtags) { selmon->sel->tags = newtags; focus(NULL); arrange(selmon); } } void toggleview(const Arg *arg) { unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; focus(NULL); arrange(selmon); } } void unfocus(Client *c, int setfocus) { if (!c) return; grabbuttons(c, 0); XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); if (setfocus) { XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); } } void unmanage(Client *c, int destroyed) { Monitor *m = c->mon; XWindowChanges wc; if (c->iskbd) updategeom(); if (c->swallowing) { unswallow(c); return; } Client *s = swallowingclient(c->win); if (s) { free(s->swallowing); s->swallowing = NULL; arrange(m); focus(NULL); return; } detach(c); detachstack(c); if (!destroyed) { wc.border_width = c->oldbw; XGrabServer(dpy); /* avoid race conditions */ XSetErrorHandler(xerrordummy); 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); if (!s) { arrange(m); focus(NULL); updateclientlist(); } } 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); } else { focus(NULL); } } void updatebars(void) { Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, .background_pixmap = ParentRelative, .event_mask = ButtonPressMask|ExposureMask|KeyPressMask|KeyReleaseMask }; XClassHint ch = {"dwm", "dwm"}; for (m = mons; m; m = m->next) { if (!m->softwin) { XSetWindowAttributes softwa = { .override_redirect = True, .background_pixmap = ParentRelative, .event_mask = ExposureMask }; int sh_bar = 26; m->softwin = XCreateWindow(dpy, root, m->wx, m->my + m->mh - sh_bar, m->ww, sh_bar, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &softwa); XDefineCursor(dpy, m->softwin, cursor[CurNormal]->cursor); XMapWindow(dpy, m->softwin); XLowerWindow(dpy, m->softwin); XSetClassHint(dpy, m->softwin, &ch); } if (m->barwin) continue; m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); XMapRaised(dpy, m->barwin); XSetClassHint(dpy, m->barwin, &ch); } } void updatebarpos(Monitor *m) { m->wy = m->my; m->wh = m->mh; if (m->showbar) { m->wh -= bh; m->by = m->topbar ? m->wy : m->wy + m->wh; m->wy = m->topbar ? m->wy + bh : m->wy; } else m->by = -bh; } void updateclientlist() { 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; if (n <= nn) { /* new monitors available */ for (i = 0; i < (nn - n); 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); } } else { /* less monitors available nn < n */ 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; if( attachbelow ) attachBelow(c); else 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); } void updatestatus(void) { size_t len; if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) strcpy(stext, "dwm-"VERSION); len = strlen(stext); while (len > 0 && (stext[len - 1] == ' ' || stext[len - 1] == '\n' || stext[len - 1] == '\r')) stext[--len] = '\0'; 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); if (c == selmon->sel) writefocusedwin(c); } void updatewindowtype(Client *c) { Atom state = getatomprop(c, netatom[NetWMState]); Atom wtype = getatomprop(c, netatom[NetWMWindowType]); if (state == netatom[NetWMFullscreen]) setfullscreen(c, 1); if (wtype == netatom[NetWMWindowTypeDialog]) c->isfloating = 1; } void updatewmhints(Client *c) { XWMHints *wmh; if ((wmh = XGetWMHints(dpy, c->win))) { if (c == selmon->sel && wmh->flags & XUrgencyHint) { wmh->flags &= ~XUrgencyHint; XSetWMHints(dpy, c->win, wmh); } else c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; if (wmh->flags & InputHint) c->neverfocus = !wmh->input; else c->neverfocus = 0; XFree(wmh); } } void view(const Arg *arg) { if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; focus(NULL); arrange(selmon); } pid_t winpid(Window w) { pid_t result = 0; xcb_res_client_id_spec_t spec = {0}; spec.client = w; spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; xcb_generic_error_t *e = NULL; xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); if (!r) return (pid_t)0; xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); for (; i.rem; xcb_res_client_id_value_next(&i)) { spec = i.data->spec; if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { uint32_t *t = xcb_res_client_id_value_value(i.data); result = *t; break; } } free(r); if (result == (pid_t)-1) result = 0; return result; } pid_t getparentprocess(pid_t p) { unsigned int v = 0; #ifdef __linux__ FILE *f; char buf[256]; snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); if (!(f = fopen(buf, "r"))) return 0; fscanf(f, "%*u %*s %*c %u", &v); fclose(f); #endif /* __linux__ */ return (pid_t)v; } int isdescprocess(pid_t p, pid_t c) { while (p != c && c != 0) c = getparentprocess(c); return (int)c; } Client * termforwin(const Client *w) { Client *c; Monitor *m; if (!w->pid || w->isterminal) return NULL; for (m = mons; m; m = m->next) { for (c = m->clients; c; c = c->next) { if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) return c; } } return NULL; } Client * swallowingclient(Window w) { Client *c; Monitor *m; for (m = mons; m; m = m->next) { for (c = m->clients; c; c = c->next) { if (c->swallowing && c->swallowing->win == w) return c; } } return NULL; } 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 || w == m->softwin) return m; if ((c = wintoclient(w))) return c->mon; return selmon; } /* There's no way to check accesses to destroyed windows, thus those cases are * ignored (especially on UnmapNotify's). Other types of errors call Xlibs * default error handler, which may call exit. */ int xerror(Display *dpy, XErrorEvent *ee) { if (ee->error_code == BadWindow || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) return 0; fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); return xerrorxlib(dpy, ee); /* may call exit */ } int xerrordummy(Display *dpy, XErrorEvent *ee) { return 0; } /* Startup Error handler to check if another window manager * is already running. */ int xerrorstart(Display *dpy, XErrorEvent *ee) { die("dwm: another window manager is already running"); return -1; } void zoom(const Arg *arg) { Client *c = selmon->sel; if (!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating)) return; if (c == nexttiled(selmon->clients)) if (!c || !(c = nexttiled(c->next))) return; pop(c); } static void set_color_slot(int scm, int col, const char *val) { if (!val || !val[0]) return; free(colors[scm][col]); colors[scm][col] = strdup(val); } static const char * xrm_lookup_any(XrmDatabase xdb, const char *k1, const char *k2, const char *k3) { char *type = NULL; XrmValue xval; if (k1 && XrmGetResource(xdb, k1, "*", &type, &xval) && xval.addr && xval.addr[0]) return xval.addr; if (k2 && XrmGetResource(xdb, k2, "*", &type, &xval) && xval.addr && xval.addr[0]) return xval.addr; if (k3 && XrmGetResource(xdb, k3, "*", &type, &xval) && xval.addr && xval.addr[0]) return xval.addr; return NULL; } void readxresources(void) { XrmInitialize(); XrmDatabase xdb = NULL; Atom actual_type = None; int actual_format = 0; unsigned long nitems = 0, bytes_after = 0; unsigned char *prop = NULL; const char *home = getenv("HOME"); char path[512]; snprintf(path, sizeof(path), "%s/.Xresources", (home && home[0]) ? home : "/home/user"); xdb = XrmGetFileDatabase(path); if (!xdb && XGetWindowProperty(dpy, root, XA_RESOURCE_MANAGER, 0L, 65536L, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && prop) { if (actual_type == XA_STRING && actual_format == 8 && nitems > 0) xdb = XrmGetStringDatabase((const char *)prop); XFree(prop); } if (!xdb) { char *xrm = XResourceManagerString(drw->dpy); if (xrm) xdb = XrmGetStringDatabase(xrm); } if (xdb) { const char *ff = xrm_lookup_any(xdb, "theme.fontFace", NULL, NULL); const char *icf = xrm_lookup_any(xdb, "theme.iconFontFace", NULL, NULL); const char *bsz = xrm_lookup_any(xdb, "theme.baseFontSize", NULL, NULL); const char *dwmf = xrm_lookup_any(xdb, "dwm.font", NULL, NULL); if (ff || bsz) { int sz = bsz ? atoi(bsz) : 15; if (sz < 8 || sz > 32) sz = 15; snprintf(active_font, sizeof(active_font), "%s, %s %dpx", ff ? ff : "Fira Code", icf ? icf : "DejaVuSansM Nerd Font", sz); } else if (dwmf) { snprintf(active_font, sizeof(active_font), "%s", dwmf); } else { snprintf(active_font, sizeof(active_font), "%s", font); } set_color_slot(SchemeNorm, ColBg, xrm_lookup_any(xdb, "theme.bgBar", "dwm.background", "dwm.normbgcolor")); set_color_slot(SchemeNorm, ColFg, xrm_lookup_any(xdb, "theme.fgMain", "dwm.foreground", "dwm.normfgcolor")); set_color_slot(SchemeNorm, ColBorder, xrm_lookup_any(xdb, "theme.border", "dwm.border", "dwm.normbordercolor")); set_color_slot(SchemeSel, ColBg, xrm_lookup_any(xdb, "theme.selBg", "dwm.selbackground", "dwm.selbgcolor")); set_color_slot(SchemeSel, ColFg, xrm_lookup_any(xdb, "theme.selFg", "dwm.selforeground", "dwm.selfgcolor")); set_color_slot(SchemeSel, ColBorder, xrm_lookup_any(xdb, "theme.accent", "dwm.selborder", "dwm.selbordercolor")); set_color_slot(SchemeTabActive, ColBg, xrm_lookup_any(xdb, "theme.selBg", "dwm.tabactivebackground", "dwm.selbgcolor")); set_color_slot(SchemeTabActive, ColFg, xrm_lookup_any(xdb, "theme.selFg", "dwm.tabactiveforeground", "dwm.selfgcolor")); set_color_slot(SchemeTabActive, ColBorder, xrm_lookup_any(xdb, "theme.accent", "dwm.tabactiveborder", "dwm.selbordercolor")); set_color_slot(SchemeTabInactive, ColBg, xrm_lookup_any(xdb, "theme.bgBar", "dwm.tabinactivebackground", "dwm.normbgcolor")); set_color_slot(SchemeTabInactive, ColFg, xrm_lookup_any(xdb, "theme.fgMain", "dwm.tabinactiveforeground", "dwm.normfgcolor")); set_color_slot(SchemeTabInactive, ColBorder, xrm_lookup_any(xdb, "theme.border", "dwm.tabinactiveborder", "dwm.normbordercolor")); XrmDestroyDatabase(xdb); } } static void reload_dwm_theme(void) { int i, j; Monitor *m; Client *c; readxresources(); for (i = 0; i <= SchemeTabInactive; ++i) { for (j = 0; j <= ColBorder; ++j) { if (!colors[i][j]) colors[i][j] = strdup(defaultcolors[i][j]); } } if (drw->font) { drw_font_free(drw->font); drw->font = NULL; } if (!drw_font_create(drw, active_font)) drw_font_create(drw, font); for (i = 0; i < LENGTH(colors); i++) { free(scheme[i]); scheme[i] = drw_scm_create(drw, (const char **)colors[i], 3); } for (j = 0; j <= SchemeTabInactive; ++j) { free(colors[j][ColFg]); free(colors[j][ColBg]); free(colors[j][ColBorder]); colors[j][ColFg] = NULL; colors[j][ColBg] = NULL; colors[j][ColBorder] = NULL; } for (m = mons; m; m = m->next) { for (c = m->clients; c; c = c->next) { if (c == m->sel) XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); else XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); } } drawbars(); } int main(int argc, char *argv[]) { XInitThreads(); if (argc == 2 && !strcmp("-v", argv[1])) die("dwm-"VERSION); else if (argc != 1) die("usage: dwm [-v]"); if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fputs("warning: no locale support\n", stderr); if (!(dpy = XOpenDisplay(NULL))) die("dwm: cannot open display"); XkbSetDetectableAutoRepeat(dpy, True, NULL); if (!(xcon = XGetXCBConnection(dpy))) die("dwm: cannot get xcb connection\n"); checkotherwm(); setup(); #ifdef __OpenBSD__ if (pledge("stdio rpath proc exec", NULL) == -1) die("pledge"); #endif /* __OpenBSD__ */ scan(); run(); cleanup(); XCloseDisplay(dpy); return EXIT_SUCCESS; } static void bstack(Monitor *m) { int w, h, mh, mx, tx, ty, tw; unsigned int i, n; Client *c; for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if (n == 0) return; if (n > m->nmaster) { mh = m->nmaster ? m->mfact * m->wh : 0; tw = m->ww / (n - m->nmaster); ty = m->wy + mh; } else { mh = m->wh; tw = m->ww; ty = m->wy; } for (i = mx = 0, tx = m->wx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { if (i < m->nmaster) { w = (m->ww - mx) / (MIN(n, m->nmaster) - i); resize(c, m->wx + mx, m->wy, w - (2 * c->bw), mh - (2 * c->bw), 0); mx += WIDTH(c); } else { h = m->wh - mh; resize(c, tx, ty, tw - (2 * c->bw), h - (2 * c->bw), 0); if (tw != m->ww) tx += WIDTH(c); } } } static void bstackhoriz(Monitor *m) { int w, mh, mx, tx, ty, th; unsigned int i, n; Client *c; for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if (n == 0) return; if (n > m->nmaster) { mh = m->nmaster ? m->mfact * m->wh : 0; th = (m->wh - mh) / (n - m->nmaster); ty = m->wy + mh; } else { th = mh = m->wh; ty = m->wy; } for (i = mx = 0, tx = m->wx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { if (i < m->nmaster) { w = (m->ww - mx) / (MIN(n, m->nmaster) - i); resize(c, m->wx + mx, m->wy, w - (2 * c->bw), mh - (2 * c->bw), 0); mx += WIDTH(c); } else { resize(c, tx, ty, m->ww - (2 * c->bw), th - (2 * c->bw), 0); if (th != m->wh) ty += HEIGHT(c); } } } void clienttagpush(const Arg *arg) { if (selmon->sel == NULL) return; if (arg->i > 0) { if ( (selmon->tagset[selmon->seltags] & TAGMASK) && (selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) ) { selmon->sel->tags <<= 1; } } else { if ( (selmon->tagset[selmon->seltags] & TAGMASK) && (selmon->tagset[selmon->seltags] & (TAGMASK << 1)) ) { selmon->sel->tags >>= 1; } } focus(NULL); arrange(selmon); } void shiftview(const Arg *arg) { Arg shifted; if(arg->i > 0) shifted.ui = (selmon->tagset[selmon->seltags] << arg->i) | (selmon->tagset[selmon->seltags] >> (LENGTH(tags) - arg->i)); else shifted.ui = selmon->tagset[selmon->seltags] >> (- arg->i) | selmon->tagset[selmon->seltags] << (LENGTH(tags) + arg->i); view(&shifted); } void unfloatvisible(const Arg *arg) { Client *c; for (c = selmon->clients; c; c = c->next) if (ISVISIBLE(c) && c->isfloating) c->isfloating = c->isfixed; if (arg && arg->v) setlayout(arg); else arrange(selmon); } void insertclient(Client *item, Client *insertItem, int after) { Client *c; if (item == NULL || insertItem == NULL || item == insertItem) return; detach(insertItem); if (!after && selmon->clients == item) { attach(insertItem); return; } if (after) { c = item; } else { for (c = selmon->clients; c; c = c->next) { if (c->next == item) break; } } insertItem->next = c->next; c->next = insertItem; } void inplacerotate(const Arg *arg) { if(!selmon->sel || (selmon->sel->isfloating && !arg->f)) return; unsigned int selidx = 0, i = 0; Client *c = NULL, *stail = NULL, *mhead = NULL, *mtail = NULL, *shead = NULL; // Determine positionings for insertclient for (c = selmon->clients; c; c = c->next) { if (ISVISIBLE(c) && !(c->isfloating)) { if (selmon->sel == c) { selidx = i; } if (i == selmon->nmaster - 1) { mtail = c; } if (i == selmon->nmaster) { shead = c; } if (mhead == NULL) { mhead = c; } stail = c; i++; } } // All clients rotate if (arg->i == 2) insertclient(selmon->clients, stail, 0); if (arg->i == -2) insertclient(stail, selmon->clients, 1); // Stack xor master rotate if (arg->i == -1 && selidx >= selmon->nmaster) insertclient(stail, shead, 1); if (arg->i == 1 && selidx >= selmon->nmaster) insertclient(shead, stail, 0); if (arg->i == -1 && selidx < selmon->nmaster) insertclient(mtail, mhead, 1); if (arg->i == 1 && selidx < selmon->nmaster) insertclient(mhead, mtail, 0); // Restore focus position i = 0; for (c = selmon->clients; c; c = c->next) { if (!ISVISIBLE(c) || (c->isfloating)) continue; if (i == selidx) { focus(c); break; } i++; } arrange(selmon); focus(c); } void transfer(const Arg *arg) { Client *c, *mtail = selmon->clients, *stail = NULL, *insertafter; int transfertostack = 0, i, nmasterclients; for (i = 0, c = selmon->clients; c; c = c->next) { if (!ISVISIBLE(c) || c->isfloating) continue; if (selmon->sel == c) { transfertostack = i < selmon->nmaster && selmon->nmaster != 0; } if (i < selmon->nmaster) { nmasterclients++; mtail = c; } stail = c; i++; } if (selmon->sel->isfloating || i == 0) { return; } else if (transfertostack) { selmon->nmaster = MIN(i, selmon->nmaster) - 1; insertafter = stail; } else { selmon->nmaster = selmon->nmaster + 1; insertafter = mtail; } if (insertafter != selmon->sel) { detach(selmon->sel); if (selmon->nmaster == 1 && !transfertostack) { attach(selmon->sel); // Head prepend case } else { selmon->sel->next = insertafter->next; insertafter->next = selmon->sel; } } arrange(selmon); } void transferall(const Arg *arg) { Client *c, *n = selmon->clients, *attachfrom = NULL; int i = 0, nstackclients = 0; while (n) { c = n; n = c->next; if (!ISVISIBLE(c) || c->isfloating) continue; if (i >= selmon->nmaster) { detach(c); if (!attachfrom) { attach(c); } else { c->next = attachfrom->next; attachfrom->next = c; } attachfrom = c; nstackclients++; } i++; } selmon->nmaster = nstackclients; arrange(selmon); } static Client * nextc(Client *c, float f) { if(!f) return nexttiled(c); for(; c && !ISVISIBLE(c); c = c->next); return c; } static Client * prevc(Client *c, float f) { Client *p, *r; for(p = selmon->clients, r = NULL; c && p && p != c; p = p->next) if((f || !p->isfloating) && ISVISIBLE(p)) r = p; return r; } static void pushup(const Arg *arg) { Client *sel = selmon->sel; Client *c; if(!sel || (sel->isfloating && !arg->f)) return; if((c = prevc(sel, arg->f))) { /* attach before c */ detach(sel); sel->next = c; if(selmon->clients == c) selmon->clients = sel; else { for(c = selmon->clients; c->next != sel->next; c = c->next); c->next = sel; } } else { /* move to the end */ for(c = sel; c->next; c = c->next); detach(sel); sel->next = NULL; c->next = sel; } focus(sel); arrange(selmon); } static void pushdown(const Arg *arg) { Client *sel = selmon->sel; Client *c; if(!sel || (sel->isfloating && !arg->f)) return; if((c = nextc(sel->next, arg->f))) { /* attach after c */ detach(sel); sel->next = c->next; c->next = sel; } else { /* move to the front */ detach(sel); attach(sel); } focus(sel); arrange(selmon); }