diff options
| author | Hiltjo Posthuma <hiltjo@codemadness.org> | 2015-05-04 21:54:46 +0200 |
|---|---|---|
| committer | Anselm R Garbe <garbeam@gmail.com> | 2015-06-27 21:47:10 +0200 |
| commit | 4b1fecd44e8376594c418663351fcb30c4e841de (patch) | |
| tree | 78db12fd7024bff8afb3c3bd8851c009837f5a34 | |
| parent | 13a529ce63364544bdc851dfd5d3aa2ef8740914 (diff) | |
Use libdraw: add Xft and fallback-fonts support to graphics lib
- libdraw, util: add drw.{c,h}, util.{c,h} and update code.
- libdraw: fix drw_rect(): use w and h parameter.
- libdraw: print errstr if last character in string was ":" (sbase).
- libdraw: drw_clr_free() allow valid free(NULL).
- config.def.h: set default font to monospace.
- cleanup() on exit.
- LICENSE: update license string for dmenu -v to 2015.
- LICENSE: add myself to LICENSE
| -rw-r--r-- | LICENSE | 1 | ||||
| -rw-r--r-- | Makefile | 15 | ||||
| -rw-r--r-- | config.def.h | 10 | ||||
| -rw-r--r-- | config.mk | 4 | ||||
| -rw-r--r-- | dmenu.c | 241 | ||||
| -rw-r--r-- | draw.c | 177 | ||||
| -rw-r--r-- | draw.h | 35 | ||||
| -rw-r--r-- | drw.c | 413 | ||||
| -rw-r--r-- | drw.h | 74 | ||||
| -rw-r--r-- | util.c | 23 | ||||
| -rw-r--r-- | util.h | 7 |
11 files changed, 684 insertions, 316 deletions
| @@ -7,6 +7,7 @@ MIT/X Consortium License | |||
| 7 | © 2009 Evan Gates <evan.gates@gmail.com> | 7 | © 2009 Evan Gates <evan.gates@gmail.com> |
| 8 | © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com> | 8 | © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com> |
| 9 | © 2006-2007 Michał Janeczek <janeczek at gmail dot com> | 9 | © 2006-2007 Michał Janeczek <janeczek at gmail dot com> |
| 10 | © 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org> | ||
| 10 | 11 | ||
| 11 | Permission is hereby granted, free of charge, to any person obtaining a | 12 | Permission is hereby granted, free of charge, to any person obtaining a |
| 12 | copy of this software and associated documentation files (the "Software"), | 13 | copy of this software and associated documentation files (the "Software"), |
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | include config.mk | 4 | include config.mk |
| 5 | 5 | ||
| 6 | SRC = dmenu.c draw.c stest.c | 6 | SRC = drw.c dmenu.c stest.c util.c |
| 7 | OBJ = ${SRC:.c=.o} | 7 | OBJ = ${SRC:.c=.o} |
| 8 | 8 | ||
| 9 | all: options dmenu stest | 9 | all: options dmenu stest |
| @@ -15,18 +15,18 @@ options: | |||
| 15 | @echo "CC = ${CC}" | 15 | @echo "CC = ${CC}" |
| 16 | 16 | ||
| 17 | .c.o: | 17 | .c.o: |
| 18 | @echo CC -c $< | 18 | @echo CC $< |
| 19 | @${CC} -c $< ${CFLAGS} | 19 | @${CC} -c ${CFLAGS} $< |
| 20 | 20 | ||
| 21 | config.h: | 21 | config.h: |
| 22 | @echo creating $@ from config.def.h | 22 | @echo creating $@ from config.def.h |
| 23 | @cp config.def.h $@ | 23 | @cp config.def.h $@ |
| 24 | 24 | ||
| 25 | ${OBJ}: config.h config.mk draw.h | 25 | ${OBJ}: config.h config.mk drw.h |
| 26 | 26 | ||
| 27 | dmenu: dmenu.o draw.o | 27 | dmenu: dmenu.o drw.o util.o |
| 28 | @echo CC -o $@ | 28 | @echo CC -o $@ |
| 29 | @${CC} -o $@ dmenu.o draw.o ${LDFLAGS} | 29 | @${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS} |
| 30 | 30 | ||
| 31 | stest: stest.o | 31 | stest: stest.o |
| 32 | @echo CC -o $@ | 32 | @echo CC -o $@ |
| @@ -39,7 +39,8 @@ clean: | |||
| 39 | dist: clean | 39 | dist: clean |
| 40 | @echo creating dist tarball | 40 | @echo creating dist tarball |
| 41 | @mkdir -p dmenu-${VERSION} | 41 | @mkdir -p dmenu-${VERSION} |
| 42 | @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_path dmenu_run stest.1 ${SRC} dmenu-${VERSION} | 42 | @cp LICENSE Makefile README config.mk dmenu.1 drw.h util.h dmenu_path \ |
| 43 | dmenu_run stest.1 ${SRC} dmenu-${VERSION} | ||
| 43 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} | 44 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} |
| 44 | @gzip dmenu-${VERSION}.tar | 45 | @gzip dmenu-${VERSION}.tar |
| 45 | @rm -rf dmenu-${VERSION} | 46 | @rm -rf dmenu-${VERSION} |
diff --git a/config.def.h b/config.def.h index c2a23fa..4e5e3e7 100644 --- a/config.def.h +++ b/config.def.h | |||
| @@ -4,8 +4,11 @@ | |||
| 4 | /* Default settings; can be overrided by command line. */ | 4 | /* Default settings; can be overrided by command line. */ |
| 5 | 5 | ||
| 6 | static Bool topbar = True; /* -b option; if False, dmenu appears at bottom */ | 6 | static Bool topbar = True; /* -b option; if False, dmenu appears at bottom */ |
| 7 | static const char *font = NULL; /* -fn option; default X11 font or font set */ | 7 | /* -fn option overrides fonts[0]; default X11 font or font set */ |
| 8 | static const char *prompt = NULL; /* -p option; prompt to the elft of input field */ | 8 | static const char *fonts[] = { |
| 9 | "monospace:size=10" | ||
| 10 | }; | ||
| 11 | static const char *prompt = NULL; /* -p option; prompt to the elft of input field */ | ||
| 9 | static const char *normbgcolor = "#222222"; /* -nb option; normal background */ | 12 | static const char *normbgcolor = "#222222"; /* -nb option; normal background */ |
| 10 | static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */ | 13 | static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */ |
| 11 | static const char *selbgcolor = "#005577"; /* -sb option; selected background */ | 14 | static const char *selbgcolor = "#005577"; /* -sb option; selected background */ |
| @@ -13,5 +16,4 @@ static const char *selfgcolor = "#eeeeee"; /* -sf option; selected foreground | |||
| 13 | static const char *outbgcolor = "#00ffff"; | 16 | static const char *outbgcolor = "#00ffff"; |
| 14 | static const char *outfgcolor = "#000000"; | 17 | static const char *outfgcolor = "#000000"; |
| 15 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ | 18 | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ |
| 16 | static unsigned int lines = 0; | 19 | static unsigned int lines = 0; |
| 17 | |||
| @@ -13,8 +13,8 @@ XINERAMALIBS = -lXinerama | |||
| 13 | XINERAMAFLAGS = -DXINERAMA | 13 | XINERAMAFLAGS = -DXINERAMA |
| 14 | 14 | ||
| 15 | # includes and libs | 15 | # includes and libs |
| 16 | INCS = -I${X11INC} | 16 | INCS = -I${X11INC} -I/usr/include/freetype2 |
| 17 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} | 17 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} -lfontconfig -lXft |
| 18 | 18 | ||
| 19 | # flags | 19 | # flags |
| 20 | CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | 20 | CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} |
| @@ -1,5 +1,6 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | 1 | /* See LICENSE file for copyright and license details. */ |
| 2 | #include <ctype.h> | 2 | #include <ctype.h> |
| 3 | #include <locale.h> | ||
| 3 | #include <stdio.h> | 4 | #include <stdio.h> |
| 4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
| 5 | #include <string.h> | 6 | #include <string.h> |
| @@ -11,12 +12,20 @@ | |||
| 11 | #ifdef XINERAMA | 12 | #ifdef XINERAMA |
| 12 | #include <X11/extensions/Xinerama.h> | 13 | #include <X11/extensions/Xinerama.h> |
| 13 | #endif | 14 | #endif |
| 14 | #include "draw.h" | 15 | #include <X11/Xft/Xft.h> |
| 15 | 16 | ||
| 17 | #include "drw.h" | ||
| 18 | #include "util.h" | ||
| 19 | |||
| 20 | /* macros */ | ||
| 16 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ | 21 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ |
| 17 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) | 22 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) |
| 18 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 23 | #define LENGTH(X) (sizeof X / sizeof X[0]) |
| 19 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | 24 | #define TEXTNW(X,N) (drw_font_getexts_width(drw->fonts[0], (X), (N))) |
| 25 | #define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h) | ||
| 26 | |||
| 27 | /* enums */ | ||
| 28 | enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ | ||
| 20 | 29 | ||
| 21 | typedef struct Item Item; | 30 | typedef struct Item Item; |
| 22 | struct Item { | 31 | struct Item { |
| @@ -28,6 +37,7 @@ struct Item { | |||
| 28 | static void appenditem(Item *item, Item **list, Item **last); | 37 | static void appenditem(Item *item, Item **list, Item **last); |
| 29 | static void calcoffsets(void); | 38 | static void calcoffsets(void); |
| 30 | static char *cistrstr(const char *s, const char *sub); | 39 | static char *cistrstr(const char *s, const char *sub); |
| 40 | static void cleanup(void); | ||
| 31 | static void drawmenu(void); | 41 | static void drawmenu(void); |
| 32 | static void grabkeyboard(void); | 42 | static void grabkeyboard(void); |
| 33 | static void insert(const char *str, ssize_t n); | 43 | static void insert(const char *str, ssize_t n); |
| @@ -44,11 +54,7 @@ static char text[BUFSIZ] = ""; | |||
| 44 | static int bh, mw, mh; | 54 | static int bh, mw, mh; |
| 45 | static int inputw, promptw; | 55 | static int inputw, promptw; |
| 46 | static size_t cursor = 0; | 56 | static size_t cursor = 0; |
| 47 | static unsigned long normcol[ColLast]; | ||
| 48 | static unsigned long selcol[ColLast]; | ||
| 49 | static unsigned long outcol[ColLast]; | ||
| 50 | static Atom clip, utf8; | 57 | static Atom clip, utf8; |
| 51 | static DC *dc; | ||
| 52 | static Item *items = NULL; | 58 | static Item *items = NULL; |
| 53 | static Item *matches, *matchend; | 59 | static Item *matches, *matchend; |
| 54 | static Item *prev, *curr, *next, *sel; | 60 | static Item *prev, *curr, *next, *sel; |
| @@ -56,6 +62,13 @@ static Window win; | |||
| 56 | static XIC xic; | 62 | static XIC xic; |
| 57 | static int mon = -1; | 63 | static int mon = -1; |
| 58 | 64 | ||
| 65 | static ClrScheme scheme[SchemeLast]; | ||
| 66 | static Display *dpy; | ||
| 67 | static int screen; | ||
| 68 | static Window root; | ||
| 69 | static Drw *drw; | ||
| 70 | static int sw, sh; /* X display screen geometry width, height */ | ||
| 71 | |||
| 59 | #include "config.h" | 72 | #include "config.h" |
| 60 | 73 | ||
| 61 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | 74 | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
| @@ -69,8 +82,8 @@ main(int argc, char *argv[]) { | |||
| 69 | for(i = 1; i < argc; i++) | 82 | for(i = 1; i < argc; i++) |
| 70 | /* these options take no arguments */ | 83 | /* these options take no arguments */ |
| 71 | if(!strcmp(argv[i], "-v")) { /* prints version information */ | 84 | if(!strcmp(argv[i], "-v")) { /* prints version information */ |
| 72 | puts("dmenu-"VERSION", © 2006-2014 dmenu engineers, see LICENSE for details"); | 85 | puts("dmenu-"VERSION", © 2006-2015 dmenu engineers, see LICENSE for details"); |
| 73 | exit(EXIT_SUCCESS); | 86 | exit(0); |
| 74 | } | 87 | } |
| 75 | else if(!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ | 88 | else if(!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ |
| 76 | topbar = False; | 89 | topbar = False; |
| @@ -90,7 +103,7 @@ main(int argc, char *argv[]) { | |||
| 90 | else if(!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ | 103 | else if(!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ |
| 91 | prompt = argv[++i]; | 104 | prompt = argv[++i]; |
| 92 | else if(!strcmp(argv[i], "-fn")) /* font or font set */ | 105 | else if(!strcmp(argv[i], "-fn")) /* font or font set */ |
| 93 | font = argv[++i]; | 106 | fonts[0] = argv[++i]; |
| 94 | else if(!strcmp(argv[i], "-nb")) /* normal background color */ | 107 | else if(!strcmp(argv[i], "-nb")) /* normal background color */ |
| 95 | normbgcolor = argv[++i]; | 108 | normbgcolor = argv[++i]; |
| 96 | else if(!strcmp(argv[i], "-nf")) /* normal foreground color */ | 109 | else if(!strcmp(argv[i], "-nf")) /* normal foreground color */ |
| @@ -102,8 +115,19 @@ main(int argc, char *argv[]) { | |||
| 102 | else | 115 | else |
| 103 | usage(); | 116 | usage(); |
| 104 | 117 | ||
| 105 | dc = initdc(); | 118 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) |
| 106 | initfont(dc, font); | 119 | fputs("warning: no locale support\n", stderr); |
| 120 | if(!(dpy = XOpenDisplay(NULL))) | ||
| 121 | die("dwm: cannot open display\n"); | ||
| 122 | screen = DefaultScreen(dpy); | ||
| 123 | root = RootWindow(dpy, screen); | ||
| 124 | sw = DisplayWidth(dpy, screen); | ||
| 125 | sh = DisplayHeight(dpy, screen); | ||
| 126 | drw = drw_create(dpy, screen, root, sw, sh); | ||
| 127 | drw_load_fonts(drw, fonts, LENGTH(fonts)); | ||
| 128 | if(!drw->fontcount) | ||
| 129 | die("No fonts could be loaded.\n"); | ||
| 130 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
| 107 | 131 | ||
| 108 | if(fast) { | 132 | if(fast) { |
| 109 | grabkeyboard(); | 133 | grabkeyboard(); |
| @@ -138,16 +162,30 @@ calcoffsets(void) { | |||
| 138 | if(lines > 0) | 162 | if(lines > 0) |
| 139 | n = lines * bh; | 163 | n = lines * bh; |
| 140 | else | 164 | else |
| 141 | n = mw - (promptw + inputw + textw(dc, "<") + textw(dc, ">")); | 165 | n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); |
| 142 | /* calculate which items will begin the next page and previous page */ | 166 | /* calculate which items will begin the next page and previous page */ |
| 143 | for(i = 0, next = curr; next; next = next->right) | 167 | for(i = 0, next = curr; next; next = next->right) |
| 144 | if((i += (lines > 0) ? bh : MIN(textw(dc, next->text), n)) > n) | 168 | if((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) |
| 145 | break; | 169 | break; |
| 146 | for(i = 0, prev = curr; prev && prev->left; prev = prev->left) | 170 | for(i = 0, prev = curr; prev && prev->left; prev = prev->left) |
| 147 | if((i += (lines > 0) ? bh : MIN(textw(dc, prev->left->text), n)) > n) | 171 | if((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) |
| 148 | break; | 172 | break; |
| 149 | } | 173 | } |
| 150 | 174 | ||
| 175 | void | ||
| 176 | cleanup(void) { | ||
| 177 | XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||
| 178 | drw_clr_free(scheme[SchemeNorm].bg); | ||
| 179 | drw_clr_free(scheme[SchemeNorm].fg); | ||
| 180 | drw_clr_free(scheme[SchemeSel].fg); | ||
| 181 | drw_clr_free(scheme[SchemeSel].bg); | ||
| 182 | drw_clr_free(scheme[SchemeOut].fg); | ||
| 183 | drw_clr_free(scheme[SchemeOut].bg); | ||
| 184 | drw_free(drw); | ||
| 185 | XSync(dpy, False); | ||
| 186 | XCloseDisplay(dpy); | ||
| 187 | } | ||
| 188 | |||
| 151 | char * | 189 | char * |
| 152 | cistrstr(const char *s, const char *sub) { | 190 | cistrstr(const char *s, const char *sub) { |
| 153 | size_t len; | 191 | size_t len; |
| @@ -162,50 +200,69 @@ void | |||
| 162 | drawmenu(void) { | 200 | drawmenu(void) { |
| 163 | int curpos; | 201 | int curpos; |
| 164 | Item *item; | 202 | Item *item; |
| 203 | int x = 0, y = 0, h = bh, w; | ||
| 165 | 204 | ||
| 166 | dc->x = 0; | 205 | drw_setscheme(drw, &scheme[SchemeNorm]); |
| 167 | dc->y = 0; | 206 | drw_rect(drw, 0, 0, mw, mh, True, 1, 1); |
| 168 | dc->h = bh; | ||
| 169 | drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol)); | ||
| 170 | 207 | ||
| 171 | if(prompt && *prompt) { | 208 | if(prompt && *prompt) { |
| 172 | dc->w = promptw; | 209 | drw_setscheme(drw, &scheme[SchemeSel]); |
| 173 | drawtext(dc, prompt, selcol); | 210 | drw_text(drw, x, 0, promptw, bh, prompt, 1); |
| 174 | dc->x = dc->w; | 211 | x += promptw; |
| 175 | } | 212 | } |
| 176 | /* draw input field */ | 213 | /* draw input field */ |
| 177 | dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw; | 214 | w = (lines > 0 || !matches) ? mw - x : inputw; |
| 178 | drawtext(dc, text, normcol); | 215 | drw_setscheme(drw, &scheme[SchemeNorm]); |
| 179 | if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w) | 216 | drw_text(drw, x, 0, w, bh, text, 0); |
| 180 | drawrect(dc, curpos, 2, 1, dc->h - 4, True, FG(dc, normcol)); | 217 | |
| 218 | if((curpos = TEXTNW(text, cursor) + bh/2 - 2) < w) { | ||
| 219 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
| 220 | drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0); | ||
| 221 | } | ||
| 181 | 222 | ||
| 182 | if(lines > 0) { | 223 | if(lines > 0) { |
| 183 | /* draw vertical list */ | 224 | /* draw vertical list */ |
| 184 | dc->w = mw - dc->x; | 225 | w = mw - x; |
| 185 | for(item = curr; item != next; item = item->right) { | 226 | for(item = curr; item != next; item = item->right) { |
| 186 | dc->y += dc->h; | 227 | y += h; |
| 187 | drawtext(dc, item->text, (item == sel) ? selcol : | 228 | if(item == sel) |
| 188 | (item->out) ? outcol : normcol); | 229 | drw_setscheme(drw, &scheme[SchemeSel]); |
| 230 | else if(item->out) | ||
| 231 | drw_setscheme(drw, &scheme[SchemeOut]); | ||
| 232 | else | ||
| 233 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
| 234 | |||
| 235 | drw_text(drw, x, y, w, bh, item->text, 0); | ||
| 189 | } | 236 | } |
| 190 | } | 237 | } |
| 191 | else if(matches) { | 238 | else if(matches) { |
| 192 | /* draw horizontal list */ | 239 | /* draw horizontal list */ |
| 193 | dc->x += inputw; | 240 | x += inputw; |
| 194 | dc->w = textw(dc, "<"); | 241 | w = TEXTW("<"); |
| 195 | if(curr->left) | 242 | if(curr->left) { |
| 196 | drawtext(dc, "<", normcol); | 243 | drw_setscheme(drw, &scheme[SchemeNorm]); |
| 244 | drw_text(drw, x, 0, w, bh, "<", 0); | ||
| 245 | } | ||
| 197 | for(item = curr; item != next; item = item->right) { | 246 | for(item = curr; item != next; item = item->right) { |
| 198 | dc->x += dc->w; | 247 | x += w; |
| 199 | dc->w = MIN(textw(dc, item->text), mw - dc->x - textw(dc, ">")); | 248 | w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); |
| 200 | drawtext(dc, item->text, (item == sel) ? selcol : | 249 | |
| 201 | (item->out) ? outcol : normcol); | 250 | if(item == sel) |
| 251 | drw_setscheme(drw, &scheme[SchemeSel]); | ||
| 252 | else if(item->out) | ||
| 253 | drw_setscheme(drw, &scheme[SchemeOut]); | ||
| 254 | else | ||
| 255 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
| 256 | drw_text(drw, x, 0, w, bh, item->text, 0); | ||
| 257 | } | ||
| 258 | w = TEXTW(">"); | ||
| 259 | x = mw - w; | ||
| 260 | if(next) { | ||
| 261 | drw_setscheme(drw, &scheme[SchemeNorm]); | ||
| 262 | drw_text(drw, x, 0, w, bh, ">", 0); | ||
| 202 | } | 263 | } |
| 203 | dc->w = textw(dc, ">"); | ||
| 204 | dc->x = mw - dc->w; | ||
| 205 | if(next) | ||
| 206 | drawtext(dc, ">", normcol); | ||
| 207 | } | 264 | } |
| 208 | mapdc(dc, win, mw, mh); | 265 | drw_map(drw, win, 0, 0, mw, mh); |
| 209 | } | 266 | } |
| 210 | 267 | ||
| 211 | void | 268 | void |
| @@ -214,12 +271,12 @@ grabkeyboard(void) { | |||
| 214 | 271 | ||
| 215 | /* try to grab keyboard, we may have to wait for another process to ungrab */ | 272 | /* try to grab keyboard, we may have to wait for another process to ungrab */ |
| 216 | for(i = 0; i < 1000; i++) { | 273 | for(i = 0; i < 1000; i++) { |
| 217 | if(XGrabKeyboard(dc->dpy, DefaultRootWindow(dc->dpy), True, | 274 | if(XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, |
| 218 | GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) | 275 | GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) |
| 219 | return; | 276 | return; |
| 220 | usleep(1000); | 277 | usleep(1000); |
| 221 | } | 278 | } |
| 222 | eprintf("cannot grab keyboard\n"); | 279 | die("cannot grab keyboard\n"); |
| 223 | } | 280 | } |
| 224 | 281 | ||
| 225 | void | 282 | void |
| @@ -276,14 +333,15 @@ keypress(XKeyEvent *ev) { | |||
| 276 | insert(NULL, nextrune(-1) - cursor); | 333 | insert(NULL, nextrune(-1) - cursor); |
| 277 | break; | 334 | break; |
| 278 | case XK_y: /* paste selection */ | 335 | case XK_y: /* paste selection */ |
| 279 | XConvertSelection(dc->dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, | 336 | XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, |
| 280 | utf8, utf8, win, CurrentTime); | 337 | utf8, utf8, win, CurrentTime); |
| 281 | return; | 338 | return; |
| 282 | case XK_Return: | 339 | case XK_Return: |
| 283 | case XK_KP_Enter: | 340 | case XK_KP_Enter: |
| 284 | break; | 341 | break; |
| 285 | case XK_bracketleft: | 342 | case XK_bracketleft: |
| 286 | exit(EXIT_FAILURE); | 343 | cleanup(); |
| 344 | exit(1); | ||
| 287 | default: | 345 | default: |
| 288 | return; | 346 | return; |
| 289 | } | 347 | } |
| @@ -330,7 +388,8 @@ keypress(XKeyEvent *ev) { | |||
| 330 | sel = matchend; | 388 | sel = matchend; |
| 331 | break; | 389 | break; |
| 332 | case XK_Escape: | 390 | case XK_Escape: |
| 333 | exit(EXIT_FAILURE); | 391 | cleanup(); |
| 392 | exit(1); | ||
| 334 | case XK_Home: | 393 | case XK_Home: |
| 335 | if(sel == matches) { | 394 | if(sel == matches) { |
| 336 | cursor = 0; | 395 | cursor = 0; |
| @@ -368,8 +427,10 @@ keypress(XKeyEvent *ev) { | |||
| 368 | case XK_Return: | 427 | case XK_Return: |
| 369 | case XK_KP_Enter: | 428 | case XK_KP_Enter: |
| 370 | puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); | 429 | puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); |
| 371 | if(!(ev->state & ControlMask)) | 430 | if(!(ev->state & ControlMask)) { |
| 372 | exit(EXIT_SUCCESS); | 431 | cleanup(); |
| 432 | exit(0); | ||
| 433 | } | ||
| 373 | if(sel) | 434 | if(sel) |
| 374 | sel->out = True; | 435 | sel->out = True; |
| 375 | break; | 436 | break; |
| @@ -413,7 +474,7 @@ match(void) { | |||
| 413 | /* separate input text into tokens to be matched individually */ | 474 | /* separate input text into tokens to be matched individually */ |
| 414 | for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " ")) | 475 | for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " ")) |
| 415 | if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) | 476 | if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) |
| 416 | eprintf("cannot realloc %u bytes\n", tokn * sizeof *tokv); | 477 | die("cannot realloc %u bytes\n", tokn * sizeof *tokv); |
| 417 | len = tokc ? strlen(tokv[0]) : 0; | 478 | len = tokc ? strlen(tokv[0]) : 0; |
| 418 | 479 | ||
| 419 | matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; | 480 | matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; |
| @@ -470,7 +531,7 @@ paste(void) { | |||
| 470 | Atom da; | 531 | Atom da; |
| 471 | 532 | ||
| 472 | /* we have been given the current selection, now insert it into input */ | 533 | /* we have been given the current selection, now insert it into input */ |
| 473 | XGetWindowProperty(dc->dpy, win, utf8, 0, (sizeof text / 4) + 1, False, | 534 | XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, |
| 474 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | 535 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); |
| 475 | insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p)); | 536 | insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p)); |
| 476 | XFree(p); | 537 | XFree(p); |
| @@ -486,18 +547,18 @@ readstdin(void) { | |||
| 486 | for(i = 0; fgets(buf, sizeof buf, stdin); i++) { | 547 | for(i = 0; fgets(buf, sizeof buf, stdin); i++) { |
| 487 | if(i+1 >= size / sizeof *items) | 548 | if(i+1 >= size / sizeof *items) |
| 488 | if(!(items = realloc(items, (size += BUFSIZ)))) | 549 | if(!(items = realloc(items, (size += BUFSIZ)))) |
| 489 | eprintf("cannot realloc %u bytes:", size); | 550 | die("cannot realloc %u bytes:", size); |
| 490 | if((p = strchr(buf, '\n'))) | 551 | if((p = strchr(buf, '\n'))) |
| 491 | *p = '\0'; | 552 | *p = '\0'; |
| 492 | if(!(items[i].text = strdup(buf))) | 553 | if(!(items[i].text = strdup(buf))) |
| 493 | eprintf("cannot strdup %u bytes:", strlen(buf)+1); | 554 | die("cannot strdup %u bytes:", strlen(buf)+1); |
| 494 | items[i].out = False; | 555 | items[i].out = False; |
| 495 | if(strlen(items[i].text) > max) | 556 | if(strlen(items[i].text) > max) |
| 496 | max = strlen(maxstr = items[i].text); | 557 | max = strlen(maxstr = items[i].text); |
| 497 | } | 558 | } |
| 498 | if(items) | 559 | if(items) |
| 499 | items[i].text = NULL; | 560 | items[i].text = NULL; |
| 500 | inputw = maxstr ? textw(dc, maxstr) : 0; | 561 | inputw = maxstr ? TEXTW(maxstr) : 0; |
| 501 | lines = MIN(lines, i); | 562 | lines = MIN(lines, i); |
| 502 | } | 563 | } |
| 503 | 564 | ||
| @@ -505,13 +566,13 @@ void | |||
| 505 | run(void) { | 566 | run(void) { |
| 506 | XEvent ev; | 567 | XEvent ev; |
| 507 | 568 | ||
| 508 | while(!XNextEvent(dc->dpy, &ev)) { | 569 | while(!XNextEvent(dpy, &ev)) { |
| 509 | if(XFilterEvent(&ev, win)) | 570 | if(XFilterEvent(&ev, win)) |
| 510 | continue; | 571 | continue; |
| 511 | switch(ev.type) { | 572 | switch(ev.type) { |
| 512 | case Expose: | 573 | case Expose: |
| 513 | if(ev.xexpose.count == 0) | 574 | if(ev.xexpose.count == 0) |
| 514 | mapdc(dc, win, mw, mh); | 575 | drw_map(drw, win, 0, 0, mw, mh); |
| 515 | break; | 576 | break; |
| 516 | case KeyPress: | 577 | case KeyPress: |
| 517 | keypress(&ev.xkey); | 578 | keypress(&ev.xkey); |
| @@ -522,7 +583,7 @@ run(void) { | |||
| 522 | break; | 583 | break; |
| 523 | case VisibilityNotify: | 584 | case VisibilityNotify: |
| 524 | if(ev.xvisibility.state != VisibilityUnobscured) | 585 | if(ev.xvisibility.state != VisibilityUnobscured) |
| 525 | XRaiseWindow(dc->dpy, win); | 586 | XRaiseWindow(dpy, win); |
| 526 | break; | 587 | break; |
| 527 | } | 588 | } |
| 528 | } | 589 | } |
| @@ -530,47 +591,45 @@ run(void) { | |||
| 530 | 591 | ||
| 531 | void | 592 | void |
| 532 | setup(void) { | 593 | setup(void) { |
| 533 | int x, y, screen = DefaultScreen(dc->dpy); | 594 | int x, y; |
| 534 | Window root = RootWindow(dc->dpy, screen); | ||
| 535 | XSetWindowAttributes swa; | 595 | XSetWindowAttributes swa; |
| 536 | XIM xim; | 596 | XIM xim; |
| 537 | #ifdef XINERAMA | 597 | #ifdef XINERAMA |
| 538 | int n; | ||
| 539 | XineramaScreenInfo *info; | 598 | XineramaScreenInfo *info; |
| 599 | Window w, pw, dw, *dws; | ||
| 600 | XWindowAttributes wa; | ||
| 601 | int a, j, di, n, i = 0, area = 0; | ||
| 602 | unsigned int du; | ||
| 540 | #endif | 603 | #endif |
| 541 | 604 | ||
| 542 | normcol[ColBG] = getcolor(dc, normbgcolor); | 605 | /* init appearance */ |
| 543 | normcol[ColFG] = getcolor(dc, normfgcolor); | 606 | scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor); |
| 544 | selcol[ColBG] = getcolor(dc, selbgcolor); | 607 | scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor); |
| 545 | selcol[ColFG] = getcolor(dc, selfgcolor); | 608 | scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor); |
| 546 | outcol[ColBG] = getcolor(dc, outbgcolor); | 609 | scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor); |
| 547 | outcol[ColFG] = getcolor(dc, outfgcolor); | 610 | scheme[SchemeOut].bg = drw_clr_create(drw, outbgcolor); |
| 611 | scheme[SchemeOut].fg = drw_clr_create(drw, outfgcolor); | ||
| 548 | 612 | ||
| 549 | clip = XInternAtom(dc->dpy, "CLIPBOARD", False); | 613 | clip = XInternAtom(dpy, "CLIPBOARD", False); |
| 550 | utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); | 614 | utf8 = XInternAtom(dpy, "UTF8_STRING", False); |
| 551 | 615 | ||
| 552 | /* calculate menu geometry */ | 616 | /* calculate menu geometry */ |
| 553 | bh = dc->font.height + 2; | 617 | bh = drw->fonts[0]->h + 2; |
| 554 | lines = MAX(lines, 0); | 618 | lines = MAX(lines, 0); |
| 555 | mh = (lines + 1) * bh; | 619 | mh = (lines + 1) * bh; |
| 556 | #ifdef XINERAMA | 620 | #ifdef XINERAMA |
| 557 | if((info = XineramaQueryScreens(dc->dpy, &n))) { | 621 | if((info = XineramaQueryScreens(dpy, &n))) { |
| 558 | int a, j, di, i = 0, area = 0; | 622 | XGetInputFocus(dpy, &w, &di); |
| 559 | unsigned int du; | ||
| 560 | Window w, pw, dw, *dws; | ||
| 561 | XWindowAttributes wa; | ||
| 562 | |||
| 563 | XGetInputFocus(dc->dpy, &w, &di); | ||
| 564 | if(mon != -1 && mon < n) | 623 | if(mon != -1 && mon < n) |
| 565 | i = mon; | 624 | i = mon; |
| 566 | if(!i && w != root && w != PointerRoot && w != None) { | 625 | if(!i && w != root && w != PointerRoot && w != None) { |
| 567 | /* find top-level window containing current input focus */ | 626 | /* find top-level window containing current input focus */ |
| 568 | do { | 627 | do { |
| 569 | if(XQueryTree(dc->dpy, (pw = w), &dw, &w, &dws, &du) && dws) | 628 | if(XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) |
| 570 | XFree(dws); | 629 | XFree(dws); |
| 571 | } while(w != root && w != pw); | 630 | } while(w != root && w != pw); |
| 572 | /* find xinerama screen with which the window intersects most */ | 631 | /* find xinerama screen with which the window intersects most */ |
| 573 | if(XGetWindowAttributes(dc->dpy, pw, &wa)) | 632 | if(XGetWindowAttributes(dpy, pw, &wa)) |
| 574 | for(j = 0; j < n; j++) | 633 | for(j = 0; j < n; j++) |
| 575 | if((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { | 634 | if((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { |
| 576 | area = a; | 635 | area = a; |
| @@ -578,7 +637,7 @@ setup(void) { | |||
| 578 | } | 637 | } |
| 579 | } | 638 | } |
| 580 | /* no focused window is on screen, so use pointer location instead */ | 639 | /* no focused window is on screen, so use pointer location instead */ |
| 581 | if(mon == -1 && !area && XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) | 640 | if(mon == -1 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) |
| 582 | for(i = 0; i < n; i++) | 641 | for(i = 0; i < n; i++) |
| 583 | if(INTERSECT(x, y, 1, 1, info[i])) | 642 | if(INTERSECT(x, y, 1, 1, info[i])) |
| 584 | break; | 643 | break; |
| @@ -592,29 +651,29 @@ setup(void) { | |||
| 592 | #endif | 651 | #endif |
| 593 | { | 652 | { |
| 594 | x = 0; | 653 | x = 0; |
| 595 | y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh; | 654 | y = topbar ? 0 : sh - mh; |
| 596 | mw = DisplayWidth(dc->dpy, screen); | 655 | mw = sw; |
| 597 | } | 656 | } |
| 598 | promptw = (prompt && *prompt) ? textw(dc, prompt) : 0; | 657 | promptw = (prompt && *prompt) ? TEXTW(prompt) : 0; |
| 599 | inputw = MIN(inputw, mw/3); | 658 | inputw = MIN(inputw, mw/3); |
| 600 | match(); | 659 | match(); |
| 601 | 660 | ||
| 602 | /* create menu window */ | 661 | /* create menu window */ |
| 603 | swa.override_redirect = True; | 662 | swa.override_redirect = True; |
| 604 | swa.background_pixel = normcol[ColBG]; | 663 | swa.background_pixel = scheme[SchemeNorm].bg->pix; |
| 605 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | 664 | swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; |
| 606 | win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0, | 665 | win = XCreateWindow(dpy, root, x, y, mw, mh, 0, |
| 607 | DefaultDepth(dc->dpy, screen), CopyFromParent, | 666 | DefaultDepth(dpy, screen), CopyFromParent, |
| 608 | DefaultVisual(dc->dpy, screen), | 667 | DefaultVisual(dpy, screen), |
| 609 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); | 668 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); |
| 610 | 669 | ||
| 611 | /* open input methods */ | 670 | /* open input methods */ |
| 612 | xim = XOpenIM(dc->dpy, NULL, NULL, NULL); | 671 | xim = XOpenIM(dpy, NULL, NULL, NULL); |
| 613 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, | 672 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, |
| 614 | XNClientWindow, win, XNFocusWindow, win, NULL); | 673 | XNClientWindow, win, XNFocusWindow, win, NULL); |
| 615 | 674 | ||
| 616 | XMapRaised(dc->dpy, win); | 675 | XMapRaised(dpy, win); |
| 617 | resizedc(dc, mw, mh); | 676 | drw_resize(drw, mw, mh); |
| 618 | drawmenu(); | 677 | drawmenu(); |
| 619 | } | 678 | } |
| 620 | 679 | ||
| @@ -622,5 +681,5 @@ void | |||
| 622 | usage(void) { | 681 | usage(void) { |
| 623 | fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" | 682 | fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" |
| 624 | " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); | 683 | " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); |
| 625 | exit(EXIT_FAILURE); | 684 | exit(1); |
| 626 | } | 685 | } |
| @@ -1,177 +0,0 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <locale.h> | ||
| 3 | #include <stdarg.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include <X11/Xlib.h> | ||
| 8 | #include "draw.h" | ||
| 9 | |||
| 10 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | ||
| 11 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
| 12 | #define DEFAULTFN "fixed" | ||
| 13 | |||
| 14 | static Bool loadfont(DC *dc, const char *fontstr); | ||
| 15 | |||
| 16 | void | ||
| 17 | drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) { | ||
| 18 | XSetForeground(dc->dpy, dc->gc, color); | ||
| 19 | if(fill) | ||
| 20 | XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w, h); | ||
| 21 | else | ||
| 22 | XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w-1, h-1); | ||
| 23 | } | ||
| 24 | |||
| 25 | void | ||
| 26 | drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { | ||
| 27 | char buf[BUFSIZ]; | ||
| 28 | size_t mn, n = strlen(text); | ||
| 29 | |||
| 30 | /* shorten text if necessary */ | ||
| 31 | for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) + dc->font.height/2 > dc->w; mn--) | ||
| 32 | if(mn == 0) | ||
| 33 | return; | ||
| 34 | memcpy(buf, text, mn); | ||
| 35 | if(mn < n) | ||
| 36 | for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.'); | ||
| 37 | |||
| 38 | drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col)); | ||
| 39 | drawtextn(dc, buf, mn, col); | ||
| 40 | } | ||
| 41 | |||
| 42 | void | ||
| 43 | drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) { | ||
| 44 | int x = dc->x + dc->font.height/2; | ||
| 45 | int y = dc->y + dc->font.ascent+1; | ||
| 46 | |||
| 47 | XSetForeground(dc->dpy, dc->gc, FG(dc, col)); | ||
| 48 | if(dc->font.set) | ||
| 49 | XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n); | ||
| 50 | else { | ||
| 51 | XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); | ||
| 52 | XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | void | ||
| 57 | eprintf(const char *fmt, ...) { | ||
| 58 | va_list ap; | ||
| 59 | |||
| 60 | va_start(ap, fmt); | ||
| 61 | vfprintf(stderr, fmt, ap); | ||
| 62 | va_end(ap); | ||
| 63 | |||
| 64 | if(fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { | ||
| 65 | fputc(' ', stderr); | ||
| 66 | perror(NULL); | ||
| 67 | } | ||
| 68 | exit(EXIT_FAILURE); | ||
| 69 | } | ||
| 70 | |||
| 71 | void | ||
| 72 | freedc(DC *dc) { | ||
| 73 | if(dc->font.set) | ||
| 74 | XFreeFontSet(dc->dpy, dc->font.set); | ||
| 75 | if(dc->font.xfont) | ||
| 76 | XFreeFont(dc->dpy, dc->font.xfont); | ||
| 77 | if(dc->canvas) | ||
| 78 | XFreePixmap(dc->dpy, dc->canvas); | ||
| 79 | XFreeGC(dc->dpy, dc->gc); | ||
| 80 | XCloseDisplay(dc->dpy); | ||
| 81 | free(dc); | ||
| 82 | } | ||
| 83 | |||
| 84 | unsigned long | ||
| 85 | getcolor(DC *dc, const char *colstr) { | ||
| 86 | Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)); | ||
| 87 | XColor color; | ||
| 88 | |||
| 89 | if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color)) | ||
| 90 | eprintf("cannot allocate color '%s'\n", colstr); | ||
| 91 | return color.pixel; | ||
| 92 | } | ||
| 93 | |||
| 94 | DC * | ||
| 95 | initdc(void) { | ||
| 96 | DC *dc; | ||
| 97 | |||
| 98 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
| 99 | fputs("no locale support\n", stderr); | ||
| 100 | if(!(dc = calloc(1, sizeof *dc))) | ||
| 101 | eprintf("cannot malloc %u bytes:", sizeof *dc); | ||
| 102 | if(!(dc->dpy = XOpenDisplay(NULL))) | ||
| 103 | eprintf("cannot open display\n"); | ||
| 104 | |||
| 105 | dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL); | ||
| 106 | XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter); | ||
| 107 | return dc; | ||
| 108 | } | ||
| 109 | |||
| 110 | void | ||
| 111 | initfont(DC *dc, const char *fontstr) { | ||
| 112 | if(!loadfont(dc, fontstr ? fontstr : DEFAULTFN)) { | ||
| 113 | if(fontstr != NULL) | ||
| 114 | fprintf(stderr, "cannot load font '%s'\n", fontstr); | ||
| 115 | if(fontstr == NULL || !loadfont(dc, DEFAULTFN)) | ||
| 116 | eprintf("cannot load font '%s'\n", DEFAULTFN); | ||
| 117 | } | ||
| 118 | dc->font.height = dc->font.ascent + dc->font.descent; | ||
| 119 | } | ||
| 120 | |||
| 121 | Bool | ||
| 122 | loadfont(DC *dc, const char *fontstr) { | ||
| 123 | char *def, **missing, **names; | ||
| 124 | int i, n; | ||
| 125 | XFontStruct **xfonts; | ||
| 126 | |||
| 127 | if(!*fontstr) | ||
| 128 | return False; | ||
| 129 | if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { | ||
| 130 | n = XFontsOfFontSet(dc->font.set, &xfonts, &names); | ||
| 131 | for(i = 0; i < n; i++) { | ||
| 132 | dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); | ||
| 133 | dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); | ||
| 134 | dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { | ||
| 138 | dc->font.ascent = dc->font.xfont->ascent; | ||
| 139 | dc->font.descent = dc->font.xfont->descent; | ||
| 140 | dc->font.width = dc->font.xfont->max_bounds.width; | ||
| 141 | } | ||
| 142 | if(missing) | ||
| 143 | XFreeStringList(missing); | ||
| 144 | return dc->font.set || dc->font.xfont; | ||
| 145 | } | ||
| 146 | |||
| 147 | void | ||
| 148 | mapdc(DC *dc, Window win, unsigned int w, unsigned int h) { | ||
| 149 | XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0); | ||
| 150 | } | ||
| 151 | |||
| 152 | void | ||
| 153 | resizedc(DC *dc, unsigned int w, unsigned int h) { | ||
| 154 | if(dc->canvas) | ||
| 155 | XFreePixmap(dc->dpy, dc->canvas); | ||
| 156 | |||
| 157 | dc->w = w; | ||
| 158 | dc->h = h; | ||
| 159 | dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h, | ||
| 160 | DefaultDepth(dc->dpy, DefaultScreen(dc->dpy))); | ||
| 161 | } | ||
| 162 | |||
| 163 | int | ||
| 164 | textnw(DC *dc, const char *text, size_t len) { | ||
| 165 | if(dc->font.set) { | ||
| 166 | XRectangle r; | ||
| 167 | |||
| 168 | XmbTextExtents(dc->font.set, text, len, NULL, &r); | ||
| 169 | return r.width; | ||
| 170 | } | ||
| 171 | return XTextWidth(dc->font.xfont, text, len); | ||
| 172 | } | ||
| 173 | |||
| 174 | int | ||
| 175 | textw(DC *dc, const char *text) { | ||
| 176 | return textnw(dc, text, strlen(text)) + dc->font.height; | ||
| 177 | } | ||
| @@ -1,35 +0,0 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | #define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG]) | ||
| 4 | #define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG]) | ||
| 5 | |||
| 6 | enum { ColBG, ColFG, ColBorder, ColLast }; | ||
| 7 | |||
| 8 | typedef struct { | ||
| 9 | int x, y, w, h; | ||
| 10 | Bool invert; | ||
| 11 | Display *dpy; | ||
| 12 | GC gc; | ||
| 13 | Pixmap canvas; | ||
| 14 | struct { | ||
| 15 | int ascent; | ||
| 16 | int descent; | ||
| 17 | int height; | ||
| 18 | int width; | ||
| 19 | XFontSet set; | ||
| 20 | XFontStruct *xfont; | ||
| 21 | } font; | ||
| 22 | } DC; /* draw context */ | ||
| 23 | |||
| 24 | void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color); | ||
| 25 | void drawtext(DC *dc, const char *text, unsigned long col[ColLast]); | ||
| 26 | void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]); | ||
| 27 | void eprintf(const char *fmt, ...); | ||
| 28 | void freedc(DC *dc); | ||
| 29 | unsigned long getcolor(DC *dc, const char *colstr); | ||
| 30 | DC *initdc(void); | ||
| 31 | void initfont(DC *dc, const char *fontstr); | ||
| 32 | void mapdc(DC *dc, Window win, unsigned int w, unsigned int h); | ||
| 33 | void resizedc(DC *dc, unsigned int w, unsigned int h); | ||
| 34 | int textnw(DC *dc, const char *text, size_t len); | ||
| 35 | int textw(DC *dc, const char *text); | ||
| @@ -0,0 +1,413 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <X11/Xlib.h> | ||
| 6 | #include <X11/Xft/Xft.h> | ||
| 7 | |||
| 8 | #include "drw.h" | ||
| 9 | #include "util.h" | ||
| 10 | |||
| 11 | #define UTF_INVALID 0xFFFD | ||
| 12 | #define UTF_SIZ 4 | ||
| 13 | |||
| 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; | ||
| 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; | ||
| 16 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; | ||
| 17 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; | ||
| 18 | |||
| 19 | static long | ||
| 20 | utf8decodebyte(const char c, size_t *i) { | ||
| 21 | for(*i = 0; *i < (UTF_SIZ + 1); ++(*i)) | ||
| 22 | if(((unsigned char)c & utfmask[*i]) == utfbyte[*i]) | ||
| 23 | return (unsigned char)c & ~utfmask[*i]; | ||
| 24 | return 0; | ||
| 25 | } | ||
| 26 | |||
| 27 | static size_t | ||
| 28 | utf8validate(long *u, size_t i) { | ||
| 29 | if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) | ||
| 30 | *u = UTF_INVALID; | ||
| 31 | for(i = 1; *u > utfmax[i]; ++i) | ||
| 32 | ; | ||
| 33 | return i; | ||
| 34 | } | ||
| 35 | |||
| 36 | static size_t | ||
| 37 | utf8decode(const char *c, long *u, size_t clen) { | ||
| 38 | size_t i, j, len, type; | ||
| 39 | long udecoded; | ||
| 40 | |||
| 41 | *u = UTF_INVALID; | ||
| 42 | if(!clen) | ||
| 43 | return 0; | ||
| 44 | udecoded = utf8decodebyte(c[0], &len); | ||
| 45 | if(!BETWEEN(len, 1, UTF_SIZ)) | ||
| 46 | return 1; | ||
| 47 | for(i = 1, j = 1; i < clen && j < len; ++i, ++j) { | ||
| 48 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | ||
| 49 | if(type != 0) | ||
| 50 | return j; | ||
| 51 | } | ||
| 52 | if(j < len) | ||
| 53 | return 0; | ||
| 54 | *u = udecoded; | ||
| 55 | utf8validate(u, len); | ||
| 56 | return len; | ||
| 57 | } | ||
| 58 | |||
| 59 | Drw * | ||
| 60 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) { | ||
| 61 | Drw *drw = (Drw *)calloc(1, sizeof(Drw)); | ||
| 62 | if(!drw) | ||
| 63 | return NULL; | ||
| 64 | drw->dpy = dpy; | ||
| 65 | drw->screen = screen; | ||
| 66 | drw->root = root; | ||
| 67 | drw->w = w; | ||
| 68 | drw->h = h; | ||
| 69 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); | ||
| 70 | drw->gc = XCreateGC(dpy, root, 0, NULL); | ||
| 71 | drw->fontcount = 0; | ||
| 72 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | ||
| 73 | return drw; | ||
| 74 | } | ||
| 75 | |||
| 76 | void | ||
| 77 | drw_resize(Drw *drw, unsigned int w, unsigned int h) { | ||
| 78 | if(!drw) | ||
| 79 | return; | ||
| 80 | drw->w = w; | ||
| 81 | drw->h = h; | ||
| 82 | if(drw->drawable != 0) | ||
| 83 | XFreePixmap(drw->dpy, drw->drawable); | ||
| 84 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); | ||
| 85 | } | ||
| 86 | |||
| 87 | void | ||
| 88 | drw_free(Drw *drw) { | ||
| 89 | size_t i; | ||
| 90 | |||
| 91 | for (i = 0; i < drw->fontcount; i++) { | ||
| 92 | drw_font_free(drw->fonts[i]); | ||
| 93 | } | ||
| 94 | XFreePixmap(drw->dpy, drw->drawable); | ||
| 95 | XFreeGC(drw->dpy, drw->gc); | ||
| 96 | free(drw); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* This function is an implementation detail. Library users should use | ||
| 100 | * drw_font_create instead. | ||
| 101 | */ | ||
| 102 | static Fnt * | ||
| 103 | drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) { | ||
| 104 | Fnt *font; | ||
| 105 | |||
| 106 | if (!(fontname || fontpattern)) | ||
| 107 | die("No font specified.\n"); | ||
| 108 | |||
| 109 | if (!(font = (Fnt *)calloc(1, sizeof(Fnt)))) | ||
| 110 | return NULL; | ||
| 111 | |||
| 112 | if (fontname) { | ||
| 113 | /* Using the pattern found at font->xfont->pattern does not yield same | ||
| 114 | * the same substitution results as using the pattern returned by | ||
| 115 | * FcNameParse; using the latter results in the desired fallback | ||
| 116 | * behaviour whereas the former just results in | ||
| 117 | * missing-character-rectangles being drawn, at least with some fonts. | ||
| 118 | */ | ||
| 119 | if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) || | ||
| 120 | !(font->pattern = FcNameParse((FcChar8 *) fontname))) { | ||
| 121 | if (font->xfont) { | ||
| 122 | XftFontClose(drw->dpy, font->xfont); | ||
| 123 | font->xfont = NULL; | ||
| 124 | } | ||
| 125 | fprintf(stderr, "error, cannot load font: '%s'\n", fontname); | ||
| 126 | } | ||
| 127 | } else if (fontpattern) { | ||
| 128 | if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { | ||
| 129 | fprintf(stderr, "error, cannot load font pattern.\n"); | ||
| 130 | } else { | ||
| 131 | font->pattern = NULL; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | if (!font->xfont) { | ||
| 136 | free(font); | ||
| 137 | return NULL; | ||
| 138 | } | ||
| 139 | |||
| 140 | font->ascent = font->xfont->ascent; | ||
| 141 | font->descent = font->xfont->descent; | ||
| 142 | font->h = font->ascent + font->descent; | ||
| 143 | font->dpy = drw->dpy; | ||
| 144 | return font; | ||
| 145 | } | ||
| 146 | |||
| 147 | Fnt* | ||
| 148 | drw_font_create(Drw *drw, const char *fontname) { | ||
| 149 | return drw_font_xcreate(drw, fontname, NULL); | ||
| 150 | } | ||
| 151 | |||
| 152 | void | ||
| 153 | drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) { | ||
| 154 | size_t i; | ||
| 155 | Fnt *font; | ||
| 156 | |||
| 157 | for (i = 0; i < fontcount; i++) { | ||
| 158 | if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { | ||
| 159 | die("Font cache exhausted.\n"); | ||
| 160 | } else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) { | ||
| 161 | drw->fonts[drw->fontcount++] = font; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | void | ||
| 167 | drw_font_free(Fnt *font) { | ||
| 168 | if(!font) | ||
| 169 | return; | ||
| 170 | if(font->pattern) | ||
| 171 | FcPatternDestroy(font->pattern); | ||
| 172 | XftFontClose(font->dpy, font->xfont); | ||
| 173 | free(font); | ||
| 174 | } | ||
| 175 | |||
| 176 | Clr * | ||
| 177 | drw_clr_create(Drw *drw, const char *clrname) { | ||
| 178 | Clr *clr; | ||
| 179 | Colormap cmap; | ||
| 180 | Visual *vis; | ||
| 181 | |||
| 182 | if(!drw) | ||
| 183 | return NULL; | ||
| 184 | clr = (Clr *)calloc(1, sizeof(Clr)); | ||
| 185 | if(!clr) | ||
| 186 | return NULL; | ||
| 187 | cmap = DefaultColormap(drw->dpy, drw->screen); | ||
| 188 | vis = DefaultVisual(drw->dpy, drw->screen); | ||
| 189 | if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb)) | ||
| 190 | die("error, cannot allocate color '%s'\n", clrname); | ||
| 191 | clr->pix = clr->rgb.pixel; | ||
| 192 | return clr; | ||
| 193 | } | ||
| 194 | |||
| 195 | void | ||
| 196 | drw_clr_free(Clr *clr) { | ||
| 197 | free(clr); | ||
| 198 | } | ||
| 199 | |||
| 200 | void | ||
| 201 | drw_setscheme(Drw *drw, ClrScheme *scheme) { | ||
| 202 | if(!drw) | ||
| 203 | return; | ||
| 204 | drw->scheme = scheme; | ||
| 205 | } | ||
| 206 | |||
| 207 | void | ||
| 208 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) { | ||
| 209 | if(!drw || !drw->scheme) | ||
| 210 | return; | ||
| 211 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix); | ||
| 212 | if(filled) | ||
| 213 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1); | ||
| 214 | else if(empty) | ||
| 215 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
| 216 | } | ||
| 217 | |||
| 218 | int | ||
| 219 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) { | ||
| 220 | char buf[1024]; | ||
| 221 | int tx, ty, th; | ||
| 222 | Extnts tex; | ||
| 223 | Colormap cmap; | ||
| 224 | Visual *vis; | ||
| 225 | XftDraw *d; | ||
| 226 | Fnt *curfont, *nextfont; | ||
| 227 | size_t i, len; | ||
| 228 | int utf8strlen, utf8charlen, render; | ||
| 229 | long utf8codepoint = 0; | ||
| 230 | const char *utf8str; | ||
| 231 | FcCharSet *fccharset; | ||
| 232 | FcPattern *fcpattern; | ||
| 233 | FcPattern *match; | ||
| 234 | XftResult result; | ||
| 235 | int charexists = 0; | ||
| 236 | |||
| 237 | if (!(render = x || y || w || h)) { | ||
| 238 | w = ~w; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (!drw || !drw->scheme) { | ||
| 242 | return 0; | ||
| 243 | } else if (render) { | ||
| 244 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix); | ||
| 245 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
| 246 | } | ||
| 247 | |||
| 248 | if (!text || !drw->fontcount) { | ||
| 249 | return 0; | ||
| 250 | } else if (render) { | ||
| 251 | cmap = DefaultColormap(drw->dpy, drw->screen); | ||
| 252 | vis = DefaultVisual(drw->dpy, drw->screen); | ||
| 253 | d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap); | ||
| 254 | } | ||
| 255 | |||
| 256 | curfont = drw->fonts[0]; | ||
| 257 | while (1) { | ||
| 258 | utf8strlen = 0; | ||
| 259 | utf8str = text; | ||
| 260 | nextfont = NULL; | ||
| 261 | while (*text) { | ||
| 262 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); | ||
| 263 | for (i = 0; i < drw->fontcount; i++) { | ||
| 264 | charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); | ||
| 265 | if (charexists) { | ||
| 266 | if (drw->fonts[i] == curfont) { | ||
| 267 | utf8strlen += utf8charlen; | ||
| 268 | text += utf8charlen; | ||
| 269 | } else { | ||
| 270 | nextfont = drw->fonts[i]; | ||
| 271 | } | ||
| 272 | break; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | if (!charexists || (nextfont && nextfont != curfont)) { | ||
| 277 | break; | ||
| 278 | } else { | ||
| 279 | charexists = 0; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | if (utf8strlen) { | ||
| 284 | drw_font_getexts(curfont, utf8str, utf8strlen, &tex); | ||
| 285 | /* shorten text if necessary */ | ||
| 286 | for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--) | ||
| 287 | drw_font_getexts(curfont, utf8str, len, &tex); | ||
| 288 | |||
| 289 | if (len) { | ||
| 290 | memcpy(buf, utf8str, len); | ||
| 291 | buf[len] = '\0'; | ||
| 292 | if(len < utf8strlen) | ||
| 293 | for(i = len; i && i > len - 3; buf[--i] = '.'); | ||
| 294 | |||
| 295 | if (render) { | ||
| 296 | th = curfont->ascent + curfont->descent; | ||
| 297 | ty = y + (h / 2) - (th / 2) + curfont->ascent; | ||
| 298 | tx = x + (h / 2); | ||
| 299 | XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); | ||
| 300 | } | ||
| 301 | |||
| 302 | x += tex.w; | ||
| 303 | w -= tex.w; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | if (!*text) { | ||
| 308 | break; | ||
| 309 | } else if (nextfont) { | ||
| 310 | charexists = 0; | ||
| 311 | curfont = nextfont; | ||
| 312 | } else { | ||
| 313 | /* Regardless of whether or not a fallback font is found, the | ||
| 314 | * character must be drawn. | ||
| 315 | */ | ||
| 316 | charexists = 1; | ||
| 317 | |||
| 318 | if (drw->fontcount >= DRW_FONT_CACHE_SIZE) { | ||
| 319 | continue; | ||
| 320 | } | ||
| 321 | |||
| 322 | fccharset = FcCharSetCreate(); | ||
| 323 | FcCharSetAddChar(fccharset, utf8codepoint); | ||
| 324 | |||
| 325 | if (!drw->fonts[0]->pattern) { | ||
| 326 | /* Refer to the comment in drw_font_xcreate for more | ||
| 327 | * information. | ||
| 328 | */ | ||
| 329 | die("The first font in the cache must be loaded from a font string.\n"); | ||
| 330 | } | ||
| 331 | |||
| 332 | fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); | ||
| 333 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); | ||
| 334 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); | ||
| 335 | |||
| 336 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); | ||
| 337 | FcDefaultSubstitute(fcpattern); | ||
| 338 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); | ||
| 339 | |||
| 340 | FcCharSetDestroy(fccharset); | ||
| 341 | FcPatternDestroy(fcpattern); | ||
| 342 | |||
| 343 | if (match) { | ||
| 344 | curfont = drw_font_xcreate(drw, NULL, match); | ||
| 345 | if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { | ||
| 346 | drw->fonts[drw->fontcount++] = curfont; | ||
| 347 | } else { | ||
| 348 | if (curfont) { | ||
| 349 | drw_font_free(curfont); | ||
| 350 | } | ||
| 351 | curfont = drw->fonts[0]; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | if (render) { | ||
| 358 | XftDrawDestroy(d); | ||
| 359 | } | ||
| 360 | |||
| 361 | return x; | ||
| 362 | } | ||
| 363 | |||
| 364 | void | ||
| 365 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { | ||
| 366 | if(!drw) | ||
| 367 | return; | ||
| 368 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); | ||
| 369 | XSync(drw->dpy, False); | ||
| 370 | } | ||
| 371 | |||
| 372 | |||
| 373 | void | ||
| 374 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) { | ||
| 375 | XGlyphInfo ext; | ||
| 376 | |||
| 377 | if(!font || !text) | ||
| 378 | return; | ||
| 379 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); | ||
| 380 | tex->h = font->h; | ||
| 381 | tex->w = ext.xOff; | ||
| 382 | } | ||
| 383 | |||
| 384 | unsigned int | ||
| 385 | drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) { | ||
| 386 | Extnts tex; | ||
| 387 | |||
| 388 | if(!font) | ||
| 389 | return -1; | ||
| 390 | drw_font_getexts(font, text, len, &tex); | ||
| 391 | return tex.w; | ||
| 392 | } | ||
| 393 | |||
| 394 | Cur * | ||
| 395 | drw_cur_create(Drw *drw, int shape) { | ||
| 396 | Cur *cur; | ||
| 397 | |||
| 398 | if(!drw) | ||
| 399 | return NULL; | ||
| 400 | cur = (Cur *)calloc(1, sizeof(Cur)); | ||
| 401 | if (!cur) | ||
| 402 | return NULL; | ||
| 403 | cur->cursor = XCreateFontCursor(drw->dpy, shape); | ||
| 404 | return cur; | ||
| 405 | } | ||
| 406 | |||
| 407 | void | ||
| 408 | drw_cur_free(Drw *drw, Cur *cursor) { | ||
| 409 | if(!drw || !cursor) | ||
| 410 | return; | ||
| 411 | XFreeCursor(drw->dpy, cursor->cursor); | ||
| 412 | free(cursor); | ||
| 413 | } | ||
| @@ -0,0 +1,74 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #define DRW_FONT_CACHE_SIZE 32 | ||
| 3 | |||
| 4 | typedef struct { | ||
| 5 | unsigned long pix; | ||
| 6 | XftColor rgb; | ||
| 7 | } Clr; | ||
| 8 | |||
| 9 | typedef struct { | ||
| 10 | Cursor cursor; | ||
| 11 | } Cur; | ||
| 12 | |||
| 13 | typedef struct { | ||
| 14 | Display *dpy; | ||
| 15 | int ascent; | ||
| 16 | int descent; | ||
| 17 | unsigned int h; | ||
| 18 | XftFont *xfont; | ||
| 19 | FcPattern *pattern; | ||
| 20 | } Fnt; | ||
| 21 | |||
| 22 | typedef struct { | ||
| 23 | Clr *fg; | ||
| 24 | Clr *bg; | ||
| 25 | Clr *border; | ||
| 26 | } ClrScheme; | ||
| 27 | |||
| 28 | typedef struct { | ||
| 29 | unsigned int w, h; | ||
| 30 | Display *dpy; | ||
| 31 | int screen; | ||
| 32 | Window root; | ||
| 33 | Drawable drawable; | ||
| 34 | GC gc; | ||
| 35 | ClrScheme *scheme; | ||
| 36 | size_t fontcount; | ||
| 37 | Fnt *fonts[DRW_FONT_CACHE_SIZE]; | ||
| 38 | } Drw; | ||
| 39 | |||
| 40 | typedef struct { | ||
| 41 | unsigned int w; | ||
| 42 | unsigned int h; | ||
| 43 | } Extnts; | ||
| 44 | |||
| 45 | /* Drawable abstraction */ | ||
| 46 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); | ||
| 47 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); | ||
| 48 | void drw_free(Drw *drw); | ||
| 49 | |||
| 50 | /* Fnt abstraction */ | ||
| 51 | Fnt *drw_font_create(Drw *drw, const char *fontname); | ||
| 52 | void drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount); | ||
| 53 | void drw_font_free(Fnt *font); | ||
| 54 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *extnts); | ||
| 55 | unsigned int drw_font_getexts_width(Fnt *font, const char *text, unsigned int len); | ||
| 56 | |||
| 57 | /* Colour abstraction */ | ||
| 58 | Clr *drw_clr_create(Drw *drw, const char *clrname); | ||
| 59 | void drw_clr_free(Clr *clr); | ||
| 60 | |||
| 61 | /* Cursor abstraction */ | ||
| 62 | Cur *drw_cur_create(Drw *drw, int shape); | ||
| 63 | void drw_cur_free(Drw *drw, Cur *cursor); | ||
| 64 | |||
| 65 | /* Drawing context manipulation */ | ||
| 66 | void drw_setfont(Drw *drw, Fnt *font); | ||
| 67 | void drw_setscheme(Drw *drw, ClrScheme *scheme); | ||
| 68 | |||
| 69 | /* Drawing functions */ | ||
| 70 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert); | ||
| 71 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert); | ||
| 72 | |||
| 73 | /* Map functions */ | ||
| 74 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); | ||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <stdarg.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | #include "util.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | die(const char *fmt, ...) { | ||
| 11 | va_list ap; | ||
| 12 | |||
| 13 | va_start(ap, fmt); | ||
| 14 | vfprintf(stderr, fmt, ap); | ||
| 15 | va_end(ap); | ||
| 16 | |||
| 17 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { | ||
| 18 | fputc(' ', stderr); | ||
| 19 | perror(NULL); | ||
| 20 | } | ||
| 21 | |||
| 22 | exit(1); | ||
| 23 | } | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) | ||
| 4 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) | ||
| 5 | #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) | ||
| 6 | |||
| 7 | void die(const char *errstr, ...); | ||
