/* * Copyright 2010 Johan Veenhuizen */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wind.h" #include "deleven.xbm" #include "delodd.xbm" static int errhandler(Display *, XErrorEvent *); static void onsignal(int); static int waitevent(void); static void usage(FILE *); static struct listener *getlistener(Window); enum runlevel runlevel = RL_STARTUP; static DEFINE_BITMAP(deleven); static DEFINE_BITMAP(delodd); struct bitmap *deletebitmap; static const char *progname; // Print X errors to standard error static Bool verbose = False; // The display name used in call to XOpenDisplay const char *displayname = NULL; // The last X error reported const char *xerror = NULL; Display *dpy; unsigned scr; Window root; unsigned long foregroundpixel; unsigned long backgroundpixel; unsigned long hlforegroundpixel; unsigned long hlbackgroundpixel; GC foreground; GC background; GC hlforeground; GC hlbackground; int lineheight; int halfleading; struct font *font; struct fontcolor *fhighlight; struct fontcolor *fnormal; Atom WM_CHANGE_STATE; Atom WM_DELETE_WINDOW; Atom WM_PROTOCOLS; Atom WM_STATE; Atom WM_TAKE_FOCUS; static int selfpipe[2] = { -1, -1 }; static struct pollfd pollfd[2]; static XContext listeners; /* * Print formatted error message */ void errorf(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } void setlistener(Window w, const struct listener *l) { if (l == NULL) XDeleteContext(dpy, w, listeners); else XSaveContext(dpy, w, listeners, (XPointer)l); } static struct listener *getlistener(Window w) { struct listener *l; if (XFindContext(dpy, w, listeners, (XPointer *)&l) == 0) return l; else return NULL; } int redirect(XEvent *e, Window w) { struct listener *l = getlistener(w); if (l == NULL) return -1; l->function(l->pointer, e); return 0; } static int errhandler(Display *dpy, XErrorEvent *e) { static char buf[128]; buf[0] = '\0'; XGetErrorText(dpy, e->error_code, buf, sizeof buf); if (verbose) errorf("Xlib: %s", buf); xerror = buf; return 0; } static void onsignal(int signo) { int e = errno; char c = signo; write(selfpipe[1], &c, 1); errno = e; } static int waitevent(void) { XSync(dpy, False); if (XQLength(dpy) > 0) return 0; for (;;) { int r; do { r = poll(pollfd, 2, -1); } while (r == -1 && (errno == EINTR || errno == EAGAIN)); if (r == -1) { errorf("poll: %s", strerror(errno)); return -1; } else if (pollfd[1].revents == POLLIN) { // signal received char c; read(pollfd[1].fd, &c, 1); return -1; } else if (pollfd[0].revents == POLLIN) { return 0; } else if (pollfd[0].revents == POLLHUP || pollfd[0].revents == POLLERR) { errorf("X connection broken"); return -1; } } } static void usage(FILE *f) { fprintf(f,"usage: %s [-hv]" " [-n number]" " [-t font]" " [-f color]" " [-b color]" " [-F color]" " [-B color]" " [display]\n", progname); } int main(int argc, char *argv[]) { progname = argv[0]; // The Xmb* functions use LC_CTYPE setlocale(LC_CTYPE, ""); srand((unsigned)time(NULL)); runlevel = RL_STARTUP; char *ftname = NULL; char *fname = "rgb:00/00/00"; char *bname = "rgb:ff/ff/ff"; char *hlfname = "rgb:00/00/00"; char *hlbname = "rgb:7f/ff/ff"; Desk ndesk = 0; int opt; while ((opt = getopt(argc, argv, "B:b:F:f:hn:t:v")) != -1) switch (opt) { case 'B': hlbname = optarg; break; case 'b': bname = optarg; break; case 'F': hlfname = optarg; break; case 'f': fname = optarg; break; case 'h': printf("%s\n", PACKAGE_STRING); usage(stdout); exit(0); case 'n': ndesk = strtoul(optarg, NULL, 10); break; case 't': ftname = optarg; break; case 'v': verbose = True; break; default: usage(stderr); exit(1); } if (optind < argc) displayname = argv[optind++]; if (optind < argc) { errorf("unexpected argument -- %s", argv[optind]); usage(stderr); exit(1); } XSetErrorHandler(errhandler); if ((dpy = XOpenDisplay(displayname)) == NULL) { errorf("cannot open display \"%s\"", XDisplayName(displayname)); exit(1); } scr = DefaultScreen(dpy); root = DefaultRootWindow(dpy); font = ftload(ftname); if (font == NULL) { errorf("cannot load font"); exit(1); } fnormal = ftloadcolor(fname); fhighlight = ftloadcolor(hlfname); if (fnormal == NULL || fhighlight == NULL) { errorf("cannot load font colors"); exit(1); } halfleading = (3 * font->size / 10) / 2; lineheight = font->size + 2 * halfleading; if (lineheight % 2 == 0) deletebitmap = &deleven; else deletebitmap = &delodd; foregroundpixel = getpixel(fname); backgroundpixel = getpixel(bname); hlforegroundpixel = getpixel(hlfname); hlbackgroundpixel = getpixel(hlbname); foreground = XCreateGC(dpy, root, GCForeground | GCBackground, &(XGCValues){ .foreground = foregroundpixel, .background = backgroundpixel }); background = XCreateGC(dpy, root, GCForeground | GCBackground, &(XGCValues){ .foreground = backgroundpixel, .background = foregroundpixel }); hlforeground = XCreateGC(dpy, root, GCForeground | GCBackground, &(XGCValues){ .foreground = hlforegroundpixel, .background = hlbackgroundpixel }); hlbackground = XCreateGC(dpy, root, GCForeground | GCBackground, &(XGCValues){ .foreground = hlbackgroundpixel, .background = hlforegroundpixel }); listeners = XUniqueContext(); if (pipe(selfpipe) == -1) { errorf("cannot create pipe: %s", strerror(errno)); exit(1); } fcntl(selfpipe[0], F_SETFL, O_NONBLOCK); fcntl(selfpipe[1], F_SETFL, O_NONBLOCK); pollfd[0].fd = ConnectionNumber(dpy); pollfd[0].events = POLLIN; pollfd[1].fd = selfpipe[0]; pollfd[1].events = POLLIN; struct sigaction sa; sigfillset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = onsignal; struct sigaction osa; sigaction(SIGHUP, NULL, &osa); if (osa.sa_handler != SIG_IGN) sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, NULL, &osa); if (osa.sa_handler != SIG_IGN) sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, NULL, &osa); if (osa.sa_handler != SIG_IGN) sigaction(SIGTERM, &sa, NULL); WM_CHANGE_STATE = XInternAtom(dpy, "WM_CHANGE_STATE", False); WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False); WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False); WM_STATE = XInternAtom(dpy, "WM_STATE", False); WM_TAKE_FOCUS = XInternAtom(dpy, "WM_TAKE_FOCUS", False); initroot(); ewmh_startwm(); mwm_startwm(); if (ndesk != 0) setndesk(ndesk); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); manageall(); refocus(CurrentTime); runlevel = RL_NORMAL; while (waitevent() != -1) { XEvent e; XNextEvent(dpy, &e); if (redirect(&e, e.xany.window) == -1) { /* * EWMH specifies some root window client * messages with a non-root event window, * so we need to redirect those manually. */ if (e.type == ClientMessage) redirect(&e, root); } restack(); } runlevel = RL_SHUTDOWN; // We make sure the focused window stays on top // when we map windows from other desktops, and // to warp the pointer so that focus is not lost. Window w = None; struct geometry g; struct client *c = getfocus(); if (c != NULL) { cpopgrp(c); restack(); w = cgetwin(c); g = cgetgeom(c); } unmanageall(); if (w != None) XWarpPointer(dpy, None, w, 0, 0, 0, 0, g.width / 2, g.height / 2); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); ewmh_stopwm(); ftfreecolor(fnormal); ftfreecolor(fhighlight); ftfree(font); XFreeGC(dpy, foreground); XFreeGC(dpy, background); XFreeGC(dpy, hlforeground); XFreeGC(dpy, hlbackground); XCloseDisplay(dpy); return 0; }