/* 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. * * Calls to fetch an X event from the event queue are blocking. Due reading * status text from standard input, a select()-driven main loop has been * implemented which selects for reads on the X connection and STDIN_FILENO to * handle all data smoothly. 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 global * doubly-linked client list, the focus history is remembered through a global * stack list. 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 #ifdef XINERAMA #include #endif /* macros */ #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask)) #define LENGTH(x) (sizeof x / sizeof x[0]) #define MAXMOD 16 #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define TAGMASK ((int)((1LL << LENGTH(tags)) - 1)) #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) #define ISVISIBLE(x) (x->tags & tagset[seltags]) /* enums */ enum { ColFG, ColBG, ColLast }; /* typedefs */ typedef unsigned int uint; typedef unsigned long ulong; typedef struct { ulong norm[ColLast]; ulong sel[ColLast]; ulong hover[ColLast]; Drawable drawable; GC gc; struct { int ascent; int descent; int height; XFontSet set; XFontStruct *xfont; } font; } DC; /* draw context */ typedef struct { uint width; KeySym keysym; int x, y, w, h; Bool sel; } Key; /* function declarations */ static void buttonpress(XEvent *e); static void cleanup(void); static void configurenotify(XEvent *e); static void destroynotify(XEvent *e); static void die(const char *errstr, ...); static void drawkeyboard(void); static void drawkey(Key *k, ulong col[ColLast]); static void expose(XEvent *e); static ulong getcolor(const char *colstr); static void initfont(const char *fontstr); static void leavenotify(XEvent *e); static void motionnotify(XEvent *e); static void run(void); static void setup(void); static int textnw(const char *text, uint len); /* variables */ static int screen; static int wx, wy, ww, wh; static void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, [ConfigureNotify] = configurenotify, [DestroyNotify] = destroynotify, [Expose] = expose, [LeaveNotify] = leavenotify, [MotionNotify] = motionnotify, }; static Display *dpy; static DC dc; static Window root, win; static Bool running = True; /* configuration, allows nested code to access above variables */ #include "config.h" void buttonpress(XEvent *e) { } void cleanup(void) { if(dc.font.set) XFreeFontSet(dpy, dc.font.set); else XFreeFont(dpy, dc.font.xfont); XFreePixmap(dpy, dc.drawable); XFreeGC(dpy, dc.gc); XDestroyWindow(dpy, win); XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); } void configurenotify(XEvent *e) { XConfigureEvent *ev = &e->xconfigure; if(ev->window == win && (ev->width != ww || ev->height != wh)) { ww = ev->width; wh = ev->height; XFreePixmap(dpy, dc.drawable); dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen)); drawkeyboard(); } } void die(const char *errstr, ...) { va_list ap; va_start(ap, errstr); vfprintf(stderr, errstr, ap); va_end(ap); exit(EXIT_FAILURE); } void drawkeyboard(void) { int rows, i, j; int x = 0, y = 0, h, base; for(i = 0, rows = 1; i < LENGTH(keys); i++) if(keys[i].keysym == 0) rows++; h = wh / rows; for(i = 0; i < LENGTH(keys); i++) { for(j = i, base = 0; j < LENGTH(keys) && keys[j].keysym != 0; j++) base += keys[j].width; for(x = 0; i < LENGTH(keys) && keys[i].keysym != 0; i++) { keys[i].x = x; keys[i].y = y; keys[i].w = keys[i].width * ww / base; keys[i].h = h; x += keys[i].w; printf("%i %i %i %i\n", x, y, keys[i].w, h); } y += h; } for(i = 0; i < LENGTH(keys); i++) { if(keys[i].keysym != 0) drawkey(&keys[i], dc.norm); } XSync(dpy, False); XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, wh, 0, 0); } void drawkey(Key *k, ulong col[ColLast]) { int x, y, h, len; XRectangle r = { k->x, k->y, k->w, k->h}; const char *text; XSetForeground(dpy, dc.gc, col[ColBG]); XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); XSetForeground(dpy, dc.gc, col[ColFG]); XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); text = XKeysymToString(k->keysym); len = strlen(text); h = dc.font.ascent + dc.font.descent; y = k->y + (k->h / 2) - (h / 2) + dc.font.ascent; x = k->x + (k->w / 2) - (textnw(text, len) / 2); if(dc.font.set) XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, text, len); else XDrawString(dpy, dc.drawable, dc.gc, x, y, text, len); } void destroynotify(XEvent *e) { } void expose(XEvent *e) { XExposeEvent *ev = &e->xexpose; if(ev->count == 0 && (ev->window == win)) drawkeyboard(); } ulong getcolor(const char *colstr) { Colormap cmap = DefaultColormap(dpy, screen); XColor color; if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) die("error, cannot allocate color '%s'\n", colstr); return color.pixel; } void initfont(const char *fontstr) { char *def, **missing; int i, n; missing = NULL; if(dc.font.set) XFreeFontSet(dpy, dc.font.set); dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); if(missing) { while(n--) fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]); XFreeStringList(missing); } if(dc.font.set) { XFontSetExtents *font_extents; XFontStruct **xfonts; char **font_names; dc.font.ascent = dc.font.descent = 0; font_extents = XExtentsOfFontSet(dc.font.set); n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent); dc.font.descent = MAX(dc.font.descent,(*xfonts)->descent); xfonts++; } } else { if(dc.font.xfont) XFreeFont(dpy, dc.font.xfont); dc.font.xfont = NULL; if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) die("error, cannot load font: '%s'\n", fontstr); dc.font.ascent = dc.font.xfont->ascent; dc.font.descent = dc.font.xfont->descent; } dc.font.height = dc.font.ascent + dc.font.descent; } void leavenotify(XEvent *e) { } void motionnotify(XEvent *e) { } void run(void) { XEvent ev; /* main event loop, also reads status text from stdin */ XSync(dpy, False); while(running) { XNextEvent(dpy, &ev); if(handler[ev.type]) (handler[ev.type])(&ev); /* call handler */ } } void setup(void) { /* init screen */ screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); initfont(font); /* init appearance */ ww = DisplayWidth(dpy, screen); wh = 200; wx = 0; wy = DisplayHeight(dpy, screen) - wh; dc.norm[ColBG] = getcolor(normbgcolor); dc.norm[ColFG] = getcolor(normfgcolor); dc.sel[ColBG] = getcolor(selbgcolor); dc.sel[ColFG] = getcolor(selfgcolor); dc.hover[ColBG] = getcolor(hovbgcolor); dc.hover[ColFG] = getcolor(hovfgcolor); dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen)); dc.gc = XCreateGC(dpy, root, 0, 0); if(!dc.font.set) XSetFont(dpy, dc.gc, dc.font.xfont->fid); win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, dc.norm[ColFG], dc.norm[ColBG]); XSelectInput(dpy, win, StructureNotifyMask | PointerMotionMask | ExposureMask); XMapRaised(dpy, win); drawkeyboard(); } int textnw(const char *text, uint len) { XRectangle r; if(dc.font.set) { XmbTextExtents(dc.font.set, text, len, NULL, &r); return r.width; } return XTextWidth(dc.font.xfont, text, len); } int main(int argc, char *argv[]) { if(argc == 2 && !strcmp("-v", argv[1])) die("svkc-"VERSION", © 2006-2008 svkbd engineers, see LICENSE for details\n"); else if(argc != 1) die("usage: svkbd [-v]\n"); if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fprintf(stderr, "warning: no locale support\n"); if(!(dpy = XOpenDisplay(0))) die("svkbd: cannot open display\n"); setup(); run(); cleanup(); XCloseDisplay(dpy); return 0; }