paulo@0: /* (C)opyright MMVI-MMVII Anselm R. Garbe paulo@0: * See LICENSE file for license details. paulo@0: */ paulo@0: paulo@0: #include "dwm.h" paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: #include paulo@0: paulo@0: /* extern */ paulo@0: paulo@0: char stext[256]; paulo@0: int screen, sx, sy, sw, sh, wax, way, waw, wah; paulo@0: unsigned int bh, ntags, numlockmask; paulo@0: Atom wmatom[WMLast], netatom[NetLast]; paulo@0: Bool *seltag; paulo@0: Bool selscreen = True; paulo@0: Client *clients = NULL; paulo@0: Client *sel = NULL; paulo@0: Client *stack = NULL; paulo@0: Cursor cursor[CurLast]; paulo@0: Display *dpy; paulo@0: DC dc = {0}; paulo@3: Window root, barwin, tbarwin; paulo@0: paulo@0: /* static */ paulo@0: paulo@0: static int (*xerrorxlib)(Display *, XErrorEvent *); paulo@0: static Bool otherwm, readin; paulo@0: static Bool running = True; paulo@0: paulo@0: static void paulo@0: cleanup(void) { paulo@0: close(STDIN_FILENO); paulo@0: while(stack) { paulo@0: if(stack->isbanned) paulo@0: XMoveWindow(dpy, stack->win, stack->x, stack->y); paulo@0: unmanage(stack); paulo@0: } paulo@0: if(dc.font.set) paulo@0: XFreeFontSet(dpy, dc.font.set); paulo@0: else paulo@0: XFreeFont(dpy, dc.font.xfont); paulo@0: XUngrabKey(dpy, AnyKey, AnyModifier, root); paulo@0: XFreePixmap(dpy, dc.drawable); paulo@0: XFreeGC(dpy, dc.gc); paulo@0: XDestroyWindow(dpy, barwin); paulo@3: if (TASKBAR) paulo@3: XDestroyWindow(dpy, tbarwin); paulo@0: XFreeCursor(dpy, cursor[CurNormal]); paulo@0: XFreeCursor(dpy, cursor[CurResize]); paulo@0: XFreeCursor(dpy, cursor[CurMove]); paulo@0: XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); paulo@0: XSync(dpy, False); paulo@0: free(seltag); paulo@0: } paulo@0: paulo@0: static unsigned long paulo@0: initcolor(const char *colstr) { paulo@0: Colormap cmap = DefaultColormap(dpy, screen); paulo@0: XColor color; paulo@0: paulo@0: if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) paulo@0: eprint("error, cannot allocate color '%s'\n", colstr); paulo@0: return color.pixel; paulo@0: } paulo@0: paulo@0: static void paulo@0: initfont(const char *fontstr) { paulo@0: char *def, **missing; paulo@0: int i, n; paulo@0: paulo@0: missing = NULL; paulo@0: if(dc.font.set) paulo@0: XFreeFontSet(dpy, dc.font.set); paulo@0: dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); paulo@0: if(missing) { paulo@0: while(n--) paulo@0: fprintf(stderr, "missing fontset: %s\n", missing[n]); paulo@0: XFreeStringList(missing); paulo@0: } paulo@0: if(dc.font.set) { paulo@0: XFontSetExtents *font_extents; paulo@0: XFontStruct **xfonts; paulo@0: char **font_names; paulo@0: dc.font.ascent = dc.font.descent = 0; paulo@0: font_extents = XExtentsOfFontSet(dc.font.set); paulo@0: n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); paulo@0: for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { paulo@0: if(dc.font.ascent < (*xfonts)->ascent) paulo@0: dc.font.ascent = (*xfonts)->ascent; paulo@0: if(dc.font.descent < (*xfonts)->descent) paulo@0: dc.font.descent = (*xfonts)->descent; paulo@0: xfonts++; paulo@0: } paulo@0: } paulo@0: else { paulo@0: if(dc.font.xfont) paulo@0: XFreeFont(dpy, dc.font.xfont); paulo@0: dc.font.xfont = NULL; paulo@0: if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) paulo@0: eprint("error, cannot load font: '%s'\n", fontstr); paulo@0: dc.font.ascent = dc.font.xfont->ascent; paulo@0: dc.font.descent = dc.font.xfont->descent; paulo@0: } paulo@0: dc.font.height = dc.font.ascent + dc.font.descent; paulo@0: } paulo@0: paulo@0: static void paulo@0: scan(void) { paulo@0: unsigned int i, num; paulo@0: Window *wins, d1, d2; paulo@0: XWindowAttributes wa; paulo@0: paulo@0: wins = NULL; paulo@0: if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { paulo@0: for(i = 0; i < num; i++) { paulo@0: if(!XGetWindowAttributes(dpy, wins[i], &wa) paulo@0: || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) paulo@0: continue; paulo@0: if(wa.map_state == IsViewable) paulo@0: manage(wins[i], &wa); paulo@0: } paulo@0: } paulo@0: if(wins) paulo@0: XFree(wins); paulo@0: } paulo@0: paulo@0: static void paulo@0: setup(void) { paulo@0: int i, j; paulo@0: unsigned int mask; paulo@0: Window w; paulo@0: XModifierKeymap *modmap; paulo@0: XSetWindowAttributes wa; paulo@0: paulo@0: /* init atoms */ paulo@0: wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); paulo@0: wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); paulo@0: wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); paulo@0: netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); paulo@0: netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); paulo@0: XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, paulo@0: PropModeReplace, (unsigned char *) netatom, NetLast); paulo@0: /* init cursors */ paulo@0: cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); paulo@0: cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); paulo@0: cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); paulo@0: /* init modifier map */ paulo@0: numlockmask = 0; paulo@0: modmap = XGetModifierMapping(dpy); paulo@0: for (i = 0; i < 8; i++) paulo@0: for (j = 0; j < modmap->max_keypermod; j++) { paulo@0: if(modmap->modifiermap[i * modmap->max_keypermod + j] paulo@0: == XKeysymToKeycode(dpy, XK_Num_Lock)) paulo@0: numlockmask = (1 << i); paulo@0: } paulo@0: XFreeModifiermap(modmap); paulo@0: /* select for events */ paulo@0: wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask paulo@0: | EnterWindowMask | LeaveWindowMask; paulo@0: wa.cursor = cursor[CurNormal]; paulo@0: XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); paulo@0: grabkeys(); paulo@0: compileregs(); paulo@0: for(ntags = 0; tags[ntags]; ntags++); paulo@0: seltag = emallocz(sizeof(Bool) * ntags); paulo@0: seltag[0] = True; paulo@0: /* style */ paulo@0: dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); paulo@0: dc.norm[ColBG] = initcolor(NORMBGCOLOR); paulo@0: dc.norm[ColFG] = initcolor(NORMFGCOLOR); paulo@0: dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); paulo@0: dc.sel[ColBG] = initcolor(SELBGCOLOR); paulo@0: dc.sel[ColFG] = initcolor(SELFGCOLOR); paulo@11: dc.inv[ColBorder] = initcolor(INVBORDERCOLOR); paulo@11: dc.inv[ColBG] = initcolor(INVBGCOLOR); paulo@11: dc.inv[ColFG] = initcolor(INVFGCOLOR); paulo@0: initfont(FONT); paulo@0: /* geometry */ paulo@0: sx = sy = 0; paulo@0: sw = DisplayWidth(dpy, screen); paulo@0: sh = DisplayHeight(dpy, screen); paulo@0: initlayouts(); paulo@0: /* bar */ paulo@0: dc.h = bh = dc.font.height + 2; paulo@0: wa.override_redirect = 1; paulo@0: wa.background_pixmap = ParentRelative; paulo@0: wa.event_mask = ButtonPressMask | ExposureMask; paulo@0: barwin = XCreateWindow(dpy, root, sx, sy + (TOPBAR ? 0 : sh - bh), sw, bh, 0, paulo@0: DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), paulo@0: CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); paulo@0: XDefineCursor(dpy, barwin, cursor[CurNormal]); paulo@0: XMapRaised(dpy, barwin); paulo@0: strcpy(stext, "dwm-"VERSION); paulo@3: /* taskbar */ paulo@3: if (TASKBAR) { paulo@3: wa.override_redirect = 1; paulo@3: wa.background_pixmap = ParentRelative; paulo@3: wa.event_mask = ButtonPressMask | ExposureMask; paulo@3: tbarwin = XCreateWindow(dpy, root, sx, sy + (TOPBAR ? sh - bh : 0 ), sw, bh, 0, paulo@3: DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), paulo@3: CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); paulo@3: XDefineCursor(dpy, tbarwin, cursor[CurNormal]); paulo@3: XMapRaised(dpy, tbarwin); paulo@3: } paulo@0: /* windowarea */ paulo@0: wax = sx; paulo@3: way = sy + (TASKBAR ? bh : (TOPBAR ? bh : 0)); paulo@3: wah = sh - bh - (TASKBAR ? bh : 0); paulo@0: waw = sw; paulo@0: /* pixmap for everything */ paulo@0: dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); paulo@0: dc.gc = XCreateGC(dpy, root, 0, 0); paulo@0: XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); paulo@0: /* multihead support */ paulo@0: selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); paulo@0: } paulo@0: paulo@0: /* paulo@0: * Startup Error handler to check if another window manager paulo@0: * is already running. paulo@0: */ paulo@0: static int paulo@0: xerrorstart(Display *dsply, XErrorEvent *ee) { paulo@0: otherwm = True; paulo@0: return -1; paulo@0: } paulo@0: paulo@0: /* extern */ paulo@0: paulo@0: void paulo@0: sendevent(Window w, Atom a, long value) { paulo@0: XEvent e; paulo@0: paulo@0: e.type = ClientMessage; paulo@0: e.xclient.window = w; paulo@0: e.xclient.message_type = a; paulo@0: e.xclient.format = 32; paulo@0: e.xclient.data.l[0] = value; paulo@0: e.xclient.data.l[1] = CurrentTime; paulo@0: XSendEvent(dpy, w, False, NoEventMask, &e); paulo@0: XSync(dpy, False); paulo@0: } paulo@0: paulo@0: void paulo@0: quit(Arg *arg) { paulo@0: readin = running = False; paulo@0: } paulo@0: paulo@0: /* There's no way to check accesses to destroyed windows, thus those cases are paulo@0: * ignored (especially on UnmapNotify's). Other types of errors call Xlibs paulo@0: * default error handler, which may call exit. paulo@0: */ paulo@0: int paulo@0: xerror(Display *dpy, XErrorEvent *ee) { paulo@0: if(ee->error_code == BadWindow paulo@0: || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) paulo@0: || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) paulo@0: || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) paulo@0: || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) paulo@0: || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) paulo@0: || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) paulo@0: || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) paulo@0: return 0; paulo@0: fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", paulo@0: ee->request_code, ee->error_code); paulo@0: return xerrorxlib(dpy, ee); /* may call exit */ paulo@0: } paulo@0: paulo@0: int paulo@0: main(int argc, char *argv[]) { paulo@0: char *p; paulo@0: int r, xfd; paulo@0: fd_set rd; paulo@0: XEvent ev; paulo@0: paulo@0: if(argc == 2 && !strncmp("-v", argv[1], 3)) paulo@0: eprint("dwm-"VERSION", (C)opyright MMVI-MMVII Anselm R. Garbe\n"); paulo@0: else if(argc != 1) paulo@0: eprint("usage: dwm [-v]\n"); paulo@0: setlocale(LC_CTYPE, ""); paulo@0: if(!(dpy = XOpenDisplay(0))) paulo@0: eprint("dwm: cannot open display\n"); paulo@0: xfd = ConnectionNumber(dpy); paulo@0: screen = DefaultScreen(dpy); paulo@0: root = RootWindow(dpy, screen); paulo@0: otherwm = False; paulo@0: XSetErrorHandler(xerrorstart); paulo@0: /* this causes an error if some other window manager is running */ paulo@0: XSelectInput(dpy, root, SubstructureRedirectMask); paulo@0: XSync(dpy, False); paulo@0: if(otherwm) paulo@0: eprint("dwm: another window manager is already running\n"); paulo@0: paulo@0: XSync(dpy, False); paulo@0: XSetErrorHandler(NULL); paulo@0: xerrorxlib = XSetErrorHandler(xerror); paulo@0: XSync(dpy, False); paulo@0: setup(); paulo@0: drawstatus(); paulo@0: scan(); paulo@0: paulo@0: /* main event loop, also reads status text from stdin */ paulo@0: XSync(dpy, False); paulo@0: readin = True; paulo@0: while(running) { paulo@0: FD_ZERO(&rd); paulo@0: if(readin) paulo@0: FD_SET(STDIN_FILENO, &rd); paulo@0: FD_SET(xfd, &rd); paulo@0: if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { paulo@0: if(errno == EINTR) paulo@0: continue; paulo@0: eprint("select failed\n"); paulo@0: } paulo@0: if(FD_ISSET(STDIN_FILENO, &rd)) { paulo@0: switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { paulo@0: case -1: paulo@0: strncpy(stext, strerror(errno), sizeof stext - 1); paulo@0: stext[sizeof stext - 1] = '\0'; paulo@0: readin = False; paulo@0: break; paulo@0: case 0: paulo@0: strncpy(stext, "EOF", 4); paulo@0: readin = False; paulo@0: break; paulo@0: default: paulo@0: for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); paulo@0: for(; p >= stext && *p != '\n'; --p); paulo@0: if(p > stext) paulo@0: strncpy(stext, p + 1, sizeof stext); paulo@0: } paulo@0: drawstatus(); paulo@0: } paulo@0: if(FD_ISSET(xfd, &rd)) paulo@0: while(XPending(dpy)) { paulo@0: XNextEvent(dpy, &ev); paulo@0: if(handler[ev.type]) paulo@0: (handler[ev.type])(&ev); /* call handler */ paulo@0: } paulo@0: } paulo@0: cleanup(); paulo@0: XCloseDisplay(dpy); paulo@0: return 0; paulo@0: }