shod

mouse-based window manager that can tile windows inside floating containers
Log | Files | Refs | README | LICENSE

xdraw.c (27581B)


      1 #include <err.h>
      2 #include <stdlib.h>
      3 
      4 #include "shod.h"
      5 
      6 static GC gc;
      7 static struct Theme {
      8 	XftFont *font;
      9 	XftColor colors[STYLE_LAST][COLOR_LAST];
     10 } theme;
     11 
     12 static int
     13 alloccolor(const char *s, XftColor *color)
     14 {
     15 	if(!XftColorAllocName(dpy, visual, colormap, s, color)) {
     16 		warnx("could not allocate color: %s", s);
     17 		return 0;
     18 	}
     19 	return 1;
     20 }
     21 
     22 static XftFont *
     23 openfont(const char *s)
     24 {
     25 	XftFont *font = NULL;
     26 
     27 	if ((font = XftFontOpenXlfd(dpy, screen, s)) == NULL)
     28 		if ((font = XftFontOpenName(dpy, screen, s)) == NULL)
     29 			warnx("could not open font: %s", s);
     30 	return font;
     31 }
     32 
     33 void
     34 pixmapnew(Pixmap *pix, Window win, int w, int h)
     35 {
     36 	if (*pix != None)
     37 		XFreePixmap(dpy, *pix);
     38 	*pix = XCreatePixmap(dpy, win, w, h, depth);
     39 }
     40 
     41 void
     42 drawcommit(Pixmap pix, Window win)
     43 {
     44 	XSetWindowBackgroundPixmap(dpy, win, pix);
     45 	XClearWindow(dpy, win);
     46 }
     47 
     48 /* draw text into drawable */
     49 void
     50 drawtitle(Drawable pix, const char *text, int w, int drawlines, int style, int pressed, int ismenu)
     51 {
     52 	XGCValues val;
     53 	XGlyphInfo box;
     54 	XftColor *color;
     55 	XftDraw *draw;
     56 	size_t len;
     57 	unsigned int top, bot;
     58 	int i, x, y;
     59 
     60 	top = theme.colors[style][pressed ? COLOR_DARK : COLOR_LIGHT].pixel;
     61 	bot = theme.colors[style][pressed ? COLOR_LIGHT : COLOR_DARK].pixel;
     62 	if (ismenu || drawlines)
     63 		color = &theme.colors[STYLE_OTHER][COLOR_FG];
     64 	else
     65 		color = &theme.colors[style][COLOR_LIGHT];
     66 	draw = XftDrawCreate(dpy, pix, visual, colormap);
     67 	len = strlen(text);
     68 	XftTextExtentsUtf8(dpy, theme.font, (FcChar8 *)text, len, &box);
     69 	x = max(0, (w - box.width) / 2 + box.x);
     70 	y = (config.titlewidth - box.height) / 2 + box.y;
     71 	for (i = 3; drawlines && i < config.titlewidth - 3; i += 3) {
     72 		val.foreground = top;
     73 		XChangeGC(dpy, gc, GCForeground, &val);
     74 		XFillRectangle(dpy, pix, gc, 4, i, x - 8, 1);
     75 		XFillRectangle(dpy, pix, gc, w - x + 2, i, x - 6, 1);
     76 	}
     77 	for (i = 4; drawlines && i < config.titlewidth - 2; i += 3) {
     78 		val.foreground = bot;
     79 		XChangeGC(dpy, gc, GCForeground, &val);
     80 		XFillRectangle(dpy, pix, gc, 4, i, x - 8, 1);
     81 		XFillRectangle(dpy, pix, gc, w - x + 2, i, x - 6, 1);
     82 	}
     83 	XftDrawStringUtf8(draw, color, theme.font, x, y, (FcChar8 *)text, len);
     84 	XftDrawDestroy(draw);
     85 }
     86 
     87 /* draw borders with shadows */
     88 void
     89 drawborders(Pixmap pix, int w, int h, int style)
     90 {
     91 	XGCValues val;
     92 	XRectangle *recs;
     93 	XftColor *decor;
     94 	int partw, parth;
     95 	int i;
     96 
     97 	if (w <= 0 || h <= 0)
     98 		return;
     99 
    100 	decor = theme.colors[style];
    101 	partw = w - 2 * config.borderwidth;
    102 	parth = h - 2 * config.borderwidth;
    103 
    104 	recs = ecalloc(config.shadowthickness * 4, sizeof(*recs));
    105 
    106 	/* draw background */
    107 	val.foreground = decor[COLOR_MID].pixel;
    108 	XChangeGC(dpy, gc, GCForeground, &val);
    109 	XFillRectangle(dpy, pix, gc, 0, 0, w, h);
    110 
    111 	/* draw light shadow */
    112 	for (i = 0; i < config.shadowthickness; i++) {
    113 		recs[i * 4 + 0] = (XRectangle){.x = i, .y = i, .width = 1, .height = h - 1 - i};
    114 		recs[i * 4 + 1] = (XRectangle){.x = i, .y = i, .width = w - 1 - i, .height = 1};
    115 		recs[i * 4 + 2] = (XRectangle){.x = w - config.borderwidth + i, .y = config.borderwidth - 1 - i, .width = 1, .height = parth + 2 * (i + 1)};
    116 		recs[i * 4 + 3] = (XRectangle){.x = config.borderwidth - 1 - i, .y = h - config.borderwidth + i, .width = partw + 2 * (i + 1), .height = 1};
    117 	}
    118 	val.foreground = decor[COLOR_LIGHT].pixel;
    119 	XChangeGC(dpy, gc, GCForeground, &val);
    120 	XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 4);
    121 
    122 	/* draw dark shadow */
    123 	for (i = 0; i < config.shadowthickness; i++) {
    124 		recs[i * 4 + 0] = (XRectangle){.x = w - 1 - i, .y = i,         .width = 1,         .height = h - i * 2};
    125 		recs[i * 4 + 1] = (XRectangle){.x = i,         .y = h - 1 - i, .width = w - i * 2, .height = 1};
    126 		recs[i * 4 + 2] = (XRectangle){.x = config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i, .width = 1,                 .height = parth + 1 + i * 2};
    127 		recs[i * 4 + 3] = (XRectangle){.x = config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i, .width = partw + 1 + i * 2, .height = 1};
    128 	}
    129 	val.foreground = decor[COLOR_DARK].pixel;
    130 	XChangeGC(dpy, gc, GCForeground, &val);
    131 	XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 4);
    132 
    133 	free(recs);
    134 }
    135 
    136 /* draw and fill rectangle */
    137 void
    138 drawbackground(Pixmap pix, int x, int y, int w, int h, int style)
    139 {
    140 	XGCValues val;
    141 
    142 	val.foreground = theme.colors[style][COLOR_MID].pixel;
    143 	XChangeGC(dpy, gc, GCForeground, &val);
    144 	XFillRectangle(dpy, pix, gc, x, y, w, h);
    145 }
    146 
    147 /* draw rectangle shadows */
    148 void
    149 drawshadow(Pixmap pix, int x, int y, int w, int h, int style, int pressed)
    150 {
    151 	XGCValues val;
    152 	XRectangle *recs;
    153 	unsigned long top, bot;
    154 	int i;
    155 
    156 	if (w <= 0 || h <= 0)
    157 		return;
    158 
    159 	top = theme.colors[style][pressed ? COLOR_DARK : COLOR_LIGHT].pixel;
    160 	bot = theme.colors[style][pressed ? COLOR_LIGHT : COLOR_DARK].pixel;
    161 	recs = ecalloc(config.shadowthickness * 2, sizeof(*recs));
    162 
    163 	/* draw light shadow */
    164 	for(i = 0; i < config.shadowthickness; i++) {
    165 		recs[i * 2]     = (XRectangle){.x = x + i, .y = y + i, .width = 1, .height = h - (i * 2 + 1)};
    166 		recs[i * 2 + 1] = (XRectangle){.x = x + i, .y = y + i, .width = w - (i * 2 + 1), .height = 1};
    167 	}
    168 	val.foreground = top;
    169 	XChangeGC(dpy, gc, GCForeground, &val);
    170 	XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 2);
    171 
    172 	/* draw dark shadow */
    173 	for(i = 0; i < config.shadowthickness; i++) {
    174 		recs[i * 2]     = (XRectangle){.x = x + w - 1 - i, .y = y + i,         .width = 1,     .height = h - i * 2};
    175 		recs[i * 2 + 1] = (XRectangle){.x = x + i,         .y = y + h - 1 - i, .width = w - i * 2, .height = 1};
    176 	}
    177 	val.foreground = bot;
    178 	XChangeGC(dpy, gc, GCForeground, &val);
    179 	XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 2);
    180 
    181 	free(recs);
    182 }
    183 
    184 void
    185 drawframe(Pixmap pix, int isshaded, int w, int h, enum Octant o, int style)
    186 {
    187 	XRectangle *recs;
    188 	XGCValues val;
    189 	XftColor *decor;
    190 	int x, y, i;
    191 
    192 	decor = theme.colors[style];
    193 	recs = ecalloc(config.shadowthickness * 5, sizeof(*recs));
    194 
    195 	/* top edge */
    196 	drawshadow(pix, config.corner, 0, w - config.corner * 2, config.borderwidth, style, o == N);
    197 
    198 	/* bottom edge */
    199 	drawshadow(pix, config.corner, h - config.borderwidth, w - config.corner * 2, config.borderwidth, style, o == S);
    200 
    201 	/* left edge */
    202 	drawshadow(pix, 0, config.corner, config.borderwidth, h - config.corner * 2, style, o == W);
    203 
    204 	/* left edge */
    205 	drawshadow(pix, w - config.borderwidth, config.corner, config.borderwidth, h - config.corner * 2, style, o == E);
    206 
    207 	if (isshaded) {
    208 		/* left corner */
    209 		x = 0;
    210 		for (i = 0; i < config.shadowthickness; i++) {
    211 			recs[i * 3 + 0] = (XRectangle){.x = x + i, .y = 0, .width = 1,                     .height = h - 1 - i};
    212 			recs[i * 3 + 1] = (XRectangle){.x = x + 0, .y = i, .width = config.corner - 1 - i, .height = 1};
    213 			recs[i * 3 + 2] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = h - config.borderwidth + i, .width = config.titlewidth, .height = 1};
    214 		}
    215 		val.foreground = (o & W) ? decor[COLOR_DARK].pixel : decor[COLOR_LIGHT].pixel;
    216 		XChangeGC(dpy, gc, GCForeground, &val);
    217 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    218 		for (i = 0; i < config.shadowthickness; i++) {
    219 			recs[i * 5 + 0] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i,    .width = 1,                         .height = h - config.borderwidth * 2 + 1 + i * 2};
    220 			recs[i * 5 + 1] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i,    .width = config.titlewidth + 1 + i, .height = 1};
    221 			recs[i * 5 + 2] = (XRectangle){.x = x + config.corner - 1 - i,      .y = i,                             .width = 1,                         .height = config.borderwidth - i};
    222 			recs[i * 5 + 3] = (XRectangle){.x = x + config.corner - 1 - i,      .y = h - config.borderwidth + i, .width = 1,                         .height = config.borderwidth - i};
    223 			recs[i * 5 + 4] = (XRectangle){.x = x + i,                          .y = h - 1 - i,                  .width = config.corner - i,         .height = 1};
    224 		}
    225 		val.foreground = (o & W) ? decor[COLOR_LIGHT].pixel : decor[COLOR_DARK].pixel;
    226 		XChangeGC(dpy, gc, GCForeground, &val);
    227 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 5);
    228 
    229 		/* right corner */
    230 		x = w - config.corner;
    231 		for (i = 0; i < config.shadowthickness; i++) {
    232 			recs[i * 5 + 0] = (XRectangle){.x = x + i,                     .y = 0,                             .width = 1,                     .height = config.borderwidth - 1 - i};
    233 			recs[i * 5 + 1] = (XRectangle){.x = x + 0,                     .y = i,                             .width = config.corner - 1 - i, .height = 1};
    234 			recs[i * 5 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = config.borderwidth - 1 - i,    .width = 1,                     .height = h - config.borderwidth * 2 + 1 + i * 2};
    235 			recs[i * 5 + 3] = (XRectangle){.x = x + i,                     .y = h - config.borderwidth + i, .width = config.titlewidth + 1, .height = 1};
    236 			recs[i * 5 + 4] = (XRectangle){.x = x + i,                     .y = h - config.borderwidth + i, .width = 1,                     .height = config.borderwidth - 1 - i * 2};
    237 		}
    238 		val.foreground = (o == E) ? decor[COLOR_DARK].pixel : decor[COLOR_LIGHT].pixel;
    239 		XChangeGC(dpy, gc, GCForeground, &val);
    240 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 5);
    241 		for (i = 0; i < config.shadowthickness; i++) {
    242 			recs[i * 3 + 0] = (XRectangle){.x = x + config.corner - 1 - i, .y = i,                          .width = 1,                 .height = h - i};
    243 			recs[i * 3 + 1] = (XRectangle){.x = x + i,                     .y = config.borderwidth - 1 - i, .width = config.titlewidth, .height = 1};
    244 			recs[i * 3 + 2] = (XRectangle){.x = x + i,                     .y = h - 1 - i,               .width = config.corner - i, .height = 1};
    245 		}
    246 		val.foreground = (o == E) ? decor[COLOR_LIGHT].pixel : decor[COLOR_DARK].pixel;
    247 		XChangeGC(dpy, gc, GCForeground, &val);
    248 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    249 	} else {
    250 		/* top left corner */
    251 		x = y = 0;
    252 		for (i = 0; i < config.shadowthickness; i++) {
    253 			recs[i * 2 + 0] = (XRectangle){.x = x + i, .y = y + 0, .width = 1,                     .height = config.corner - 1 - i};
    254 			recs[i * 2 + 1] = (XRectangle){.x = x + 0, .y = y + i, .width = config.corner - 1 - i, .height = 1};
    255 		}
    256 		val.foreground = (o == NW) ? decor[COLOR_DARK].pixel : decor[COLOR_LIGHT].pixel;
    257 		XChangeGC(dpy, gc, GCForeground, &val);
    258 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 2);
    259 		for (i = 0; i < config.shadowthickness; i++) {
    260 			recs[i * 4 + 0] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + config.borderwidth - 1 - i, .width = 1,                         .height = config.titlewidth + 1 + i};
    261 			recs[i * 4 + 1] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + config.borderwidth - 1 - i, .width = config.titlewidth + 1 + i, .height = 1};
    262 			recs[i * 4 + 2] = (XRectangle){.x = x + config.corner - 1 - i,      .y = y + i,                          .width = 1,                         .height = config.borderwidth - i};
    263 			recs[i * 4 + 3] = (XRectangle){.x = x + i,                          .y = y + config.corner - 1 - i,      .width = config.borderwidth - i,    .height = 1};
    264 		}
    265 		val.foreground = (o == NW) ? decor[COLOR_LIGHT].pixel : decor[COLOR_DARK].pixel;
    266 		XChangeGC(dpy, gc, GCForeground, &val);
    267 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 4);
    268 
    269 		/* bottom left corner */
    270 		x = 0;
    271 		y = h - config.corner;
    272 		for (i = 0; i < config.shadowthickness; i++) {
    273 			recs[i * 3 + 0] = (XRectangle){.x = x + i,                          .y = y + 0,                     .width = 1,                          .height = config.corner - 1 - i};
    274 			recs[i * 3 + 1] = (XRectangle){.x = x + 0,                          .y = y + i,                     .width = config.borderwidth - 1 - i, .height = 1};
    275 			recs[i * 3 + 2] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + config.titlewidth + i, .width = config.titlewidth,          .height = 1};
    276 		}
    277 		val.foreground = (o == SW) ? decor[COLOR_DARK].pixel : decor[COLOR_LIGHT].pixel;
    278 		XChangeGC(dpy, gc, GCForeground, &val);
    279 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    280 		for (i = 0; i < config.shadowthickness; i++) {
    281 			recs[i * 3 + 0] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + i,                     .width = 1,                 .height = config.titlewidth};
    282 			recs[i * 3 + 1] = (XRectangle){.x = x + i,                          .y = y + config.corner - 1 - i, .width = config.corner - i, .height = 1};
    283 			recs[i * 3 + 2] = (XRectangle){.x = x + config.corner - 1 - i,      .y = y + config.titlewidth + i, .width = 1,                 .height = config.borderwidth - i};
    284 		}
    285 		val.foreground = (o == SW) ? decor[COLOR_LIGHT].pixel : decor[COLOR_DARK].pixel;
    286 		XChangeGC(dpy, gc, GCForeground, &val);
    287 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    288 
    289 		/* top right corner */
    290 		x = w - config.corner;
    291 		y = 0;
    292 		for (i = 0; i < config.shadowthickness; i++) {
    293 			recs[i * 3 + 0] = (XRectangle){.x = x + i,                     .y = y + 0,                          .width = 1,                     .height = config.borderwidth - 1 - i};
    294 			recs[i * 3 + 1] = (XRectangle){.x = x + 0,                     .y = y + i,                          .width = config.corner - 1 - i, .height = 1};
    295 			recs[i * 3 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = y + config.borderwidth - 1 - i, .width = 1,                     .height = config.titlewidth};
    296 		}
    297 		val.foreground = (o == NE) ? decor[COLOR_DARK].pixel : decor[COLOR_LIGHT].pixel;
    298 		XChangeGC(dpy, gc, GCForeground, &val);
    299 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    300 		for (i = 0; i < config.shadowthickness; i++) {
    301 			recs[i * 3 + 0] = (XRectangle){.x = x + config.corner - 1 - i, .y = y + i,                          .width = 1,                      .height = config.corner};
    302 			recs[i * 3 + 1] = (XRectangle){.x = x + i,                     .y = y + config.borderwidth - 1 - i, .width = config.titlewidth,      .height = 1};
    303 			recs[i * 3 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = y + config.corner - 1 - i,      .width = config.borderwidth - i, .height = 1};
    304 		}
    305 		val.foreground = (o == NE) ? decor[COLOR_LIGHT].pixel : decor[COLOR_DARK].pixel;
    306 		XChangeGC(dpy, gc, GCForeground, &val);
    307 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    308 
    309 		/* bottom right corner */
    310 		x = w - config.corner;
    311 		y = h - config.corner;
    312 		for (i = 0; i < config.shadowthickness; i++) {
    313 			recs[i * 4 + 0] = (XRectangle){.x = x + i,                     .y = y + config.titlewidth + i,  .width = 1,                              .height = config.borderwidth - 1 - i * 2};
    314 			recs[i * 4 + 1] = (XRectangle){.x = x + config.titlewidth + i, .y = y + i,                      .width = config.borderwidth - 1 - i * 2, .height = 1};
    315 			recs[i * 4 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = y + i,                      .width = 1,                              .height = config.titlewidth + 1};
    316 			recs[i * 4 + 3] = (XRectangle){.x = x + i,                     .y = y + config.titlewidth + i,  .width = config.titlewidth + 1,          .height = 1};
    317 		}
    318 		val.foreground = (o == SE) ? decor[COLOR_DARK].pixel : decor[COLOR_LIGHT].pixel;
    319 		XChangeGC(dpy, gc, GCForeground, &val);
    320 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 4);
    321 		for (i = 0; i < config.shadowthickness; i++) {
    322 			recs[i * 2 + 0] = (XRectangle){.x = x + config.corner - 1 - i, .y = y + i,                     .width = 1,                      .height = config.corner - i};
    323 			recs[i * 2 + 1] = (XRectangle){.x = x + i,                     .y = y + config.corner - 1 - i, .width = config.corner - i,      .height = 1};
    324 		}
    325 		val.foreground = (o == SE) ? decor[COLOR_LIGHT].pixel : decor[COLOR_DARK].pixel;
    326 		XChangeGC(dpy, gc, GCForeground, &val);
    327 		XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 2);
    328 	}
    329 	free(recs);
    330 }
    331 
    332 void
    333 drawprompt(Pixmap pix, int w, int h)
    334 {
    335 	XGCValues val;
    336 	XRectangle *recs;
    337 	int i, partw, parth;
    338 
    339 	recs = ecalloc(config.shadowthickness * 3, sizeof(*recs));
    340 	partw = w - 2 * config.borderwidth;
    341 	parth = h - 2 * config.borderwidth;
    342 
    343 	/* draw light shadow */
    344 	for (i = 0; i < config.shadowthickness; i++) {
    345 		recs[i * 3 + 0] = (XRectangle){.x = i,                          .y = i,                          .width = 1,                 .height = h - 1 - i};
    346 		recs[i * 3 + 1] = (XRectangle){.x = w - config.borderwidth + i, .y = 0,                          .width = 1,                 .height = parth + config.borderwidth + i};
    347 		recs[i * 3 + 2] = (XRectangle){.x = config.borderwidth - 1 - i, .y = h - config.borderwidth + i, .width = partw + 2 + i * 2, .height = 1};
    348 	}
    349 	val.foreground = theme.colors[FOCUSED][COLOR_LIGHT].pixel;
    350 	XChangeGC(dpy, gc, GCForeground, &val);
    351 	XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    352 
    353 	/* draw dark shadow */
    354 	for (i = 0; i < config.shadowthickness; i++) {
    355 		recs[i * 3 + 0] = (XRectangle){.x = w - 1 - i,                  .y = i,         .width = 1,         .height = h - i * 2};
    356 		recs[i * 3 + 1] = (XRectangle){.x = i,                          .y = h - 1 - i, .width = w - i * 2, .height = 1};
    357 		recs[i * 3 + 2] = (XRectangle){.x = config.borderwidth - 1 - i, .y = i,         .width = 1,         .height = parth + config.borderwidth};
    358 	}
    359 	val.foreground = theme.colors[FOCUSED][COLOR_DARK].pixel;
    360 	XChangeGC(dpy, gc, GCForeground, &val);
    361 	XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 3);
    362 
    363 	free(recs);
    364 }
    365 
    366 void
    367 drawdock(Pixmap pix, int w, int h)
    368 {
    369 	XGCValues val;
    370 	XRectangle *recs;
    371 	int i;
    372 
    373 	if (pix == None || w <= 0 || h <= 0)
    374 		return;
    375 	recs = ecalloc(DOCKBORDER * 3, sizeof(*recs));
    376 
    377 	val.foreground = theme.colors[STYLE_OTHER][COLOR_BG].pixel;
    378 	XChangeGC(dpy, gc, GCForeground, &val);
    379 	XFillRectangle(dpy, pix, gc, 0, 0, w, h);
    380 
    381 	val.foreground = theme.colors[STYLE_OTHER][COLOR_BORD].pixel;
    382 	XChangeGC(dpy, gc, GCForeground, &val);
    383 
    384 	if (config.dockgravity[0] != '\0' && (config.dockgravity[1] == 'F' || config.dockgravity[1] == 'f')) {
    385 		switch (config.dockgravity[0]) {
    386 		case 'N':
    387 			XFillRectangle(dpy, pix, gc, 0, h - DOCKBORDER, w, DOCKBORDER);
    388 			break;
    389 		case 'S':
    390 			XFillRectangle(dpy, pix, gc, 0, 0, w, 1);
    391 			break;
    392 		case 'W':
    393 			XFillRectangle(dpy, pix, gc, w - DOCKBORDER, 0, DOCKBORDER, h);
    394 			break;
    395 		default:
    396 		case 'E':
    397 			XFillRectangle(dpy, pix, gc, 0, 0, DOCKBORDER, h);
    398 			break;
    399 		}
    400 		return;
    401 	}
    402 
    403 	switch (config.dockgravity[0]) {
    404 	case 'N':
    405 		for(i = 0; i < DOCKBORDER; i++) {
    406 			recs[i * 3 + 0] = (XRectangle){
    407 				.x = i,
    408 				.y = 0,
    409 				.width = 1,
    410 				.height = h
    411 			};
    412 			recs[i * 3 + 1] = (XRectangle){
    413 				.x = 0,
    414 				.y = h - 1 - i,
    415 				.width = w,
    416 				.height = 1
    417 			};
    418 			recs[i * 3 + 2] = (XRectangle){
    419 				.x = w - 1 - i,
    420 				.y = 0,
    421 				.width = 1,
    422 				.height = h
    423 			};
    424 		}
    425 		break;
    426 	case 'W':
    427 		for(i = 0; i < DOCKBORDER; i++) {
    428 			recs[i * 3 + 0] = (XRectangle){
    429 				.x = 0,
    430 				.y = i,
    431 				.width = w,
    432 				.height = 1
    433 			};
    434 			recs[i * 3 + 1] = (XRectangle){
    435 				.x = w - 1 - i,
    436 				.y = 0,
    437 				.width = 1,
    438 				.height = h
    439 			};
    440 			recs[i * 3 + 2] = (XRectangle){
    441 				.x = 0,
    442 				.y = h - 1 - i,
    443 				.width = w,
    444 				.height = 1
    445 			};
    446 		}
    447 		break;
    448 	case 'S':
    449 		for(i = 0; i < DOCKBORDER; i++) {
    450 			recs[i * 3 + 0] = (XRectangle){
    451 				.x = i,
    452 				.y = 0,
    453 				.width = 1,
    454 				.height = h
    455 			};
    456 			recs[i * 3 + 1] = (XRectangle){
    457 				.x = 0,
    458 				.y = i,
    459 				.width = w,
    460 				.height = 1
    461 			};
    462 			recs[i * 3 + 2] = (XRectangle){
    463 				.x = w - 1 - i,
    464 				.y = 0,
    465 				.width = 1,
    466 				.height = h
    467 			};
    468 		}
    469 		break;
    470 	default:
    471 	case 'E':
    472 		for(i = 0; i < DOCKBORDER; i++) {
    473 			recs[i * 3 + 0] = (XRectangle){
    474 				.x = 0,
    475 				.y = i,
    476 				.width = w,
    477 				.height = 1
    478 			};
    479 			recs[i * 3 + 1] = (XRectangle){
    480 				.x = i,
    481 				.y = 0,
    482 				.width = 1,
    483 				.height = h
    484 			};
    485 			recs[i * 3 + 2] = (XRectangle){
    486 				.x = 0,
    487 				.y = h - 1 - i,
    488 				.width = w,
    489 				.height = 1
    490 			};
    491 		}
    492 		break;
    493 	}
    494 	XFillRectangles(dpy, pix, gc, recs, DOCKBORDER * 3);
    495 	free(recs);
    496 }
    497 
    498 /* draw title bar buttons */
    499 void
    500 buttonleftdecorate(Window button, Pixmap pix, int style, int pressed)
    501 {
    502 	XGCValues val;
    503 	XRectangle recs[2];
    504 	unsigned long top, bot;
    505 	int x, y, w;
    506 
    507 	w = config.titlewidth - 9;
    508 	if (pressed) {
    509 		top = theme.colors[style][COLOR_DARK].pixel;
    510 		bot = theme.colors[style][COLOR_LIGHT].pixel;
    511 	} else {
    512 		top = theme.colors[style][COLOR_LIGHT].pixel;
    513 		bot = theme.colors[style][COLOR_DARK].pixel;
    514 	}
    515 
    516 	/* draw background */
    517 	drawbackground(pix, 0, 0, config.titlewidth, config.titlewidth, style);
    518 	drawshadow(pix, 0, 0, config.titlewidth, config.titlewidth, style, pressed);
    519 
    520 	if (w > 0) {
    521 		x = 4;
    522 		y = config.titlewidth / 2 - 1;
    523 		recs[0] = (XRectangle){.x = x, .y = y, .width = w, .height = 1};
    524 		recs[1] = (XRectangle){.x = x, .y = y, .width = 1, .height = 3};
    525 		val.foreground = (pressed) ? bot : top;
    526 		XChangeGC(dpy, gc, GCForeground, &val);
    527 		XFillRectangles(dpy, pix, gc, recs, 2);
    528 		recs[0] = (XRectangle){.x = x + 1, .y = y + 2, .width = w, .height = 1};
    529 		recs[1] = (XRectangle){.x = x + w, .y = y, .width = 1, .height = 3};
    530 		val.foreground = (pressed) ? top : bot;
    531 		XChangeGC(dpy, gc, GCForeground, &val);
    532 		XFillRectangles(dpy, pix, gc, recs, 2);
    533 	}
    534 
    535 	drawcommit(pix, button);
    536 }
    537 
    538 /* draw title bar buttons */
    539 void
    540 buttonrightdecorate(Window button, Pixmap pix, int style, int pressed)
    541 {
    542 	XGCValues val;
    543 	XPoint pts[9];
    544 	unsigned long mid, top, bot;
    545 	int w;
    546 
    547 	w = (config.titlewidth - 11) / 2;
    548 	mid = theme.colors[style][COLOR_MID].pixel;
    549 	if (pressed) {
    550 		top = theme.colors[style][COLOR_DARK].pixel;
    551 		bot = theme.colors[style][COLOR_LIGHT].pixel;
    552 	} else {
    553 		top = theme.colors[style][COLOR_LIGHT].pixel;
    554 		bot = theme.colors[style][COLOR_DARK].pixel;
    555 	}
    556 
    557 	/* draw background */
    558 	val.foreground = mid;
    559 	XChangeGC(dpy, gc, GCForeground, &val);
    560 	XFillRectangle(dpy, pix, gc, 0, 0, config.titlewidth, config.titlewidth);
    561 
    562 	drawshadow(pix, 0, 0, config.titlewidth, config.titlewidth, style, pressed);
    563 
    564 	if (w > 0) {
    565 		pts[0] = (XPoint){.x = 3, .y = config.titlewidth - 5};
    566 		pts[1] = (XPoint){.x = 0, .y = - 1};
    567 		pts[2] = (XPoint){.x = w, .y = -w};
    568 		pts[3] = (XPoint){.x = -w, .y = -w};
    569 		pts[4] = (XPoint){.x = 0, .y = -2};
    570 		pts[5] = (XPoint){.x = 2, .y = 0};
    571 		pts[6] = (XPoint){.x = w, .y = w};
    572 		pts[7] = (XPoint){.x = w, .y = -w};
    573 		pts[8] = (XPoint){.x = 1, .y = 0};
    574 		val.foreground = (pressed) ? bot : top;
    575 		XChangeGC(dpy, gc, GCForeground, &val);
    576 		XDrawLines(dpy, pix, gc, pts, 9, CoordModePrevious);
    577 
    578 		pts[0] = (XPoint){.x = 3, .y = config.titlewidth - 4};
    579 		pts[1] = (XPoint){.x = 2, .y = 0};
    580 		pts[2] = (XPoint){.x = w, .y = -w};
    581 		pts[3] = (XPoint){.x = w, .y = w};
    582 		pts[4] = (XPoint){.x = 2, .y = 0};
    583 		pts[5] = (XPoint){.x = 0, .y = -2};
    584 		pts[6] = (XPoint){.x = -w, .y = -w};
    585 		pts[7] = (XPoint){.x = w, .y = -w};
    586 		pts[8] = (XPoint){.x = 0, .y = -2};
    587 		val.foreground = (pressed) ? top : bot;
    588 		XChangeGC(dpy, gc, GCForeground, &val);
    589 		XDrawLines(dpy, pix, gc, pts, 9, CoordModePrevious);
    590 	}
    591 
    592 	drawcommit(pix, button);
    593 }
    594 
    595 static void
    596 reloadtheme(void)
    597 {
    598 	Pixmap pix;
    599 
    600 	pix = XCreatePixmap(
    601 		dpy,
    602 		wm.dragwin,
    603 		2 * config.borderwidth + config.titlewidth,
    604 		2 * config.borderwidth + config.titlewidth,
    605 		depth
    606 	);
    607 	config.corner = config.borderwidth + config.titlewidth;
    608 	config.divwidth = config.borderwidth;
    609 	wm.minsize = config.corner * 2 + 10;
    610 	drawbackground(
    611 		pix,
    612 		0, 0,
    613 		2 * config.borderwidth + config.titlewidth,
    614 		2 * config.borderwidth + config.titlewidth,
    615 		FOCUSED
    616 	);
    617 	drawborders(
    618 		pix,
    619 		2 * config.borderwidth + config.titlewidth,
    620 		2 * config.borderwidth + config.titlewidth,
    621 		FOCUSED
    622 	);
    623 	drawshadow(
    624 		pix,
    625 		config.borderwidth,
    626 		config.borderwidth,
    627 		config.titlewidth,
    628 		config.titlewidth,
    629 		FOCUSED,
    630 		0
    631 	);
    632 	XResizeWindow(
    633 		dpy,
    634 		wm.dragwin,
    635 		2 * config.borderwidth + config.titlewidth,
    636 		2 * config.borderwidth + config.titlewidth
    637 	);
    638 	XSetWindowBackgroundPixmap(dpy, wm.dragwin, pix);
    639 	XClearWindow(dpy, wm.dragwin);
    640 	XFreePixmap(dpy, pix);
    641 }
    642 
    643 /* initialize decoration pixmap */
    644 int
    645 settheme(void)
    646 {
    647 	int i, j, error;
    648 
    649 	error = 0;
    650 	gc = XCreateGC(dpy, wm.dragwin, GCFillStyle, &(XGCValues){.fill_style = FillSolid});
    651 	for (i = 0; i < STYLE_LAST; i++)
    652 		for (j = 0; j < COLOR_LAST; j++)
    653 			if (!alloccolor(config.colors[i][j], &theme.colors[i][j]))
    654 				error = 1;
    655 	if ((theme.font = openfont(config.font)) == NULL)
    656 		error = 1;
    657 	if (error)
    658 		return 0;
    659 	reloadtheme();
    660 	return 1;
    661 }
    662 
    663 /* free font */
    664 void
    665 cleantheme(void)
    666 {
    667 	int i, j;
    668 
    669 	XftFontClose(dpy, theme.font);
    670 	for (i = 0; i < STYLE_LAST; i++)
    671 		for (j = 0; j < COLOR_LAST; j++)
    672 			XftColorFree(dpy, visual, colormap, &theme.colors[i][j]);
    673 	XFreeGC(dpy, gc);
    674 }
    675 
    676 static char *
    677 queryrdb(int res)
    678 {
    679 	XrmClass class[] = { wm.application.class, wm.resources[res].class, NULLQUARK };
    680 	XrmName name[] = { wm.application.name, wm.resources[res].name, NULLQUARK };
    681 
    682 	return getresource(xdb, class, name);
    683 }
    684 
    685 static void
    686 setcolor(char *value, int style, int ncolor)
    687 {
    688 	XftColor color;
    689 
    690 	if (!alloccolor(value, &color))
    691 		return;
    692 	XftColorFree(dpy, visual, colormap, &theme.colors[style][ncolor]);
    693 	theme.colors[style][ncolor] = color;
    694 }
    695 
    696 void
    697 setresources(char *xrm)
    698 {
    699 	XftFont *font;
    700 	long n;
    701 	char *value;
    702 	enum Resource resource;
    703 
    704 	xdb = NULL;
    705 	if (xrm == NULL || (xdb = XrmGetStringDatabase(xrm)) == NULL)
    706 		return;
    707 	for (resource = 0; resource < NRESOURCES; resource++) {
    708 		value = queryrdb(resource);
    709 		if (value == NULL)
    710 			continue;
    711 		switch (resource) {
    712 		case RES_FACE_NAME:
    713 			if ((font = openfont(value)) != NULL) {
    714 				XftFontClose(dpy, theme.font);
    715 				theme.font = font;
    716 			}
    717 			break;
    718 		case RES_FOREGROUND:
    719 			setcolor(value, STYLE_OTHER, COLOR_FG);
    720 			break;
    721 		case RES_DOCK_BACKGROUND:
    722 			setcolor(value, STYLE_OTHER, COLOR_BG);
    723 			break;
    724 		case RES_DOCK_BORDER:
    725 			setcolor(value, STYLE_OTHER, COLOR_BORD);
    726 			break;
    727 		case RES_ACTIVE_BG:
    728 			setcolor(value, FOCUSED, COLOR_MID);
    729 			break;
    730 		case RES_ACTIVE_TOP:
    731 			setcolor(value, FOCUSED, COLOR_LIGHT);
    732 			break;
    733 		case RES_ACTIVE_BOT:
    734 			setcolor(value, FOCUSED, COLOR_DARK);
    735 			break;
    736 		case RES_INACTIVE_BG:
    737 			setcolor(value, UNFOCUSED, COLOR_MID);
    738 			break;
    739 		case RES_INACTIVE_TOP:
    740 			setcolor(value, UNFOCUSED, COLOR_LIGHT);
    741 			break;
    742 		case RES_INACTIVE_BOT:
    743 			setcolor(value, UNFOCUSED, COLOR_DARK);
    744 			break;
    745 		case RES_URGENT_BG:
    746 			setcolor(value, URGENT, COLOR_MID);
    747 			break;
    748 		case RES_URGENT_TOP:
    749 			setcolor(value, URGENT, COLOR_LIGHT);
    750 			break;
    751 		case RES_URGENT_BOT:
    752 			setcolor(value, URGENT, COLOR_DARK);
    753 			break;
    754 		case RES_BORDER_WIDTH:
    755 			if ((n = strtol(value, NULL, 10)) > 0 && n < 100)
    756 				config.borderwidth = n;
    757 			break;
    758 		case RES_SHADOW_WIDTH:
    759 			if ((n = strtol(value, NULL, 10)) > 0 && n < 100)
    760 				config.shadowthickness = n;
    761 			break;
    762 		case RES_TITLE_WIDTH:
    763 			if ((n = strtol(value, NULL, 10)) > 0 && n < 100)
    764 				config.titlewidth = n;
    765 			break;
    766 		case RES_DOCK_WIDTH:
    767 			if ((n = strtol(value, NULL, 10)) > 0)
    768 				config.dockwidth = n;
    769 			break;
    770 		case RES_DOCK_SPACE:
    771 			if ((n = strtol(value, NULL, 10)) > 0)
    772 				config.dockspace = n;
    773 			break;
    774 		case RES_DOCK_GRAVITY:
    775 			config.dockgravity = value;
    776 			break;
    777 		case RES_NOTIFY_GAP:
    778 			if ((n = strtol(value, NULL, 10)) > 0)
    779 				config.notifgap = n;
    780 			break;
    781 		case RES_NOTIFY_GRAVITY:
    782 			config.notifgravity = value;
    783 			break;
    784 		case RES_SNAP_PROXIMITY:
    785 			if ((n = strtol(value, NULL, 10)) >= 0 && n < 100)
    786 				config.snap = n;
    787 			break;
    788 		case RES_MOVE_TIME:
    789 			if ((n = strtol(value, NULL, 10)) > 0)
    790 				config.movetime = n;
    791 			break;
    792 		case RES_RESIZE_TIME:
    793 			if ((n = strtol(value, NULL, 10)) > 0)
    794 				config.resizetime = n;
    795 			break;
    796 		default:
    797 			break;
    798 		}
    799 	}
    800 	reloadtheme();
    801 }