/* * 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 eventlistener *geteventlistener(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 eventcontext; /* * 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 *xmalloc(size_t size) { void *p; while ((p = malloc(size)) == NULL && size != 0) sleep(1); return p; } void *xrealloc(const void *p, size_t size) { void *q; while ((q = realloc((void *)p, size)) == NULL && size != 0) sleep(1); return q; } char *xstrdup(const char *s) { return strcpy(xmalloc(strlen(s) + 1), s); } unsigned long getpixel(const char *name) { XColor tc, sc; XAllocNamedColor(dpy, DefaultColormap(dpy, scr), name, &sc, &tc); return sc.pixel; } void seteventlistener(Window w, const struct eventlistener *listener) { if (listener == NULL) XDeleteContext(dpy, w, eventcontext); else XSaveContext(dpy, w, eventcontext, (XPointer)listener); } static struct eventlistener *geteventlistener(Window w) { struct eventlistener *listener = NULL; if (XFindContext(dpy, w, eventcontext, (XPointer *)&listener) == 0) return listener; else return NULL; } int redirect(XEvent *e, Window w) { struct eventlistener *listener = geteventlistener(w); if (listener != NULL) { listener->function(listener->pointer, e); return 0; } else return -1; } static int errhandler(Display *dpy, XErrorEvent *e) { static char buf[128]; buf[0] = '\0'; XGetErrorText(dpy, e->error_code, buf, sizeof buf); if (verbose) fprintf(stderr, "%s: Xlib: %s\n", progname, buf); xerror = buf; return 0; } static void onsignal(int signo) { int e = errno; char c = signo; write(selfpipe[1], &c, 1); errno = e; } void bye(void) { // Simulate SIGTERM. The real signal might be ignored. onsignal(SIGTERM); } 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 %s\n", WM_OFFICIAL_NAME, PACKAGE_VERSION); 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 }); eventcontext = 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; // Always ignore hangup. sa.sa_handler = SIG_IGN; sigaction(SIGHUP, &sa, NULL); // Install handlers for unignored signals. sa.sa_handler = onsignal; struct sigaction osa; 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(); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); if (w != None) XWarpPointer(dpy, None, w, 0, 0, 0, 0, g.width / 2, g.height / 2); ewmh_stopwm(); ftfreecolor(fnormal); ftfreecolor(fhighlight); ftfree(font); XFreeGC(dpy, foreground); XFreeGC(dpy, background); XFreeGC(dpy, hlforeground); XFreeGC(dpy, hlbackground); XCloseDisplay(dpy); return 0; }