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 }