diff options
| -rw-r--r-- | Makefile | 17 | ||||
| -rw-r--r-- | dinput.c | 387 | ||||
| -rw-r--r-- | dmenu.c | 219 | ||||
| -rw-r--r-- | draw.c | 143 |
4 files changed, 561 insertions, 205 deletions
| @@ -3,10 +3,10 @@ | |||
| 3 | 3 | ||
| 4 | include config.mk | 4 | include config.mk |
| 5 | 5 | ||
| 6 | SRC = dmenu.c | 6 | SRC = dinput.c dmenu.c draw.c |
| 7 | OBJ = ${SRC:.c=.o} | 7 | OBJ = ${SRC:.c=.o} |
| 8 | 8 | ||
| 9 | all: options dmenu | 9 | all: options dinput dmenu |
| 10 | 10 | ||
| 11 | options: | 11 | options: |
| 12 | @echo dmenu build options: | 12 | @echo dmenu build options: |
| @@ -18,19 +18,19 @@ options: | |||
| 18 | @echo CC $< | 18 | @echo CC $< |
| 19 | @${CC} -c ${CFLAGS} $< | 19 | @${CC} -c ${CFLAGS} $< |
| 20 | 20 | ||
| 21 | ${OBJ}: config.h config.mk | 21 | ${OBJ}: config.h config.mk draw.c |
| 22 | 22 | ||
| 23 | config.h: | 23 | config.h: |
| 24 | @echo creating $@ from config.def.h | 24 | @echo creating $@ from config.def.h |
| 25 | @cp config.def.h $@ | 25 | @cp config.def.h $@ |
| 26 | 26 | ||
| 27 | dmenu: ${OBJ} | 27 | .o: |
| 28 | @echo CC -o $@ | 28 | @echo CC -o $@ |
| 29 | @${CC} -o $@ ${OBJ} ${LDFLAGS} | 29 | @${CC} -o $@ $< ${LDFLAGS} |
| 30 | 30 | ||
| 31 | clean: | 31 | clean: |
| 32 | @echo cleaning | 32 | @echo cleaning |
| 33 | @rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz | 33 | @rm -f dinput dmenu ${OBJ} dmenu-${VERSION}.tar.gz |
| 34 | 34 | ||
| 35 | dist: clean | 35 | dist: clean |
| 36 | @echo creating dist tarball | 36 | @echo creating dist tarball |
| @@ -43,7 +43,8 @@ dist: clean | |||
| 43 | install: all | 43 | install: all |
| 44 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin | 44 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin |
| 45 | @mkdir -p ${DESTDIR}${PREFIX}/bin | 45 | @mkdir -p ${DESTDIR}${PREFIX}/bin |
| 46 | @cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin | 46 | @cp -f dinput dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin |
| 47 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dinput | ||
| 47 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu | 48 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu |
| 48 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path | 49 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path |
| 49 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run | 50 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run |
| @@ -55,7 +56,7 @@ install: all | |||
| 55 | uninstall: | 56 | uninstall: |
| 56 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin | 57 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin |
| 57 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path | 58 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path |
| 58 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_run | 59 | @rm -f ${DESTDIR}${PREFIX}/bin/dinput ${DESTDIR}${PREFIX}/bin/dmenu_run |
| 59 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 | 60 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 |
| 60 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 61 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 |
| 61 | 62 | ||
diff --git a/dinput.c b/dinput.c new file mode 100644 index 0000000..490380d --- /dev/null +++ b/dinput.c | |||
| @@ -0,0 +1,387 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <locale.h> | ||
| 4 | #include <stdarg.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include <strings.h> | ||
| 9 | #include <unistd.h> | ||
| 10 | #include <X11/keysym.h> | ||
| 11 | #include <X11/Xlib.h> | ||
| 12 | #include <X11/Xutil.h> | ||
| 13 | #ifdef XINERAMA | ||
| 14 | #include <X11/extensions/Xinerama.h> | ||
| 15 | #endif | ||
| 16 | |||
| 17 | /* macros */ | ||
| 18 | #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) | ||
| 19 | #define INRECT(X,Y,RX,RY,RW,RH) ((X) >= (RX) && (X) < (RX) + (RW) && (Y) >= (RY) && (Y) < (RY) + (RH)) | ||
| 20 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | ||
| 21 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | ||
| 22 | #define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) | ||
| 23 | |||
| 24 | /* forward declarations */ | ||
| 25 | static void cleanup(void); | ||
| 26 | static void drawcursor(void); | ||
| 27 | static void drawinput(void); | ||
| 28 | static void eprint(const char *errstr, ...); | ||
| 29 | static Bool grabkeyboard(void); | ||
| 30 | static void kpress(XKeyEvent * e); | ||
| 31 | static void run(void); | ||
| 32 | static void setup(Bool topbar); | ||
| 33 | |||
| 34 | #include "config.h" | ||
| 35 | |||
| 36 | /* variables */ | ||
| 37 | static char *prompt = NULL; | ||
| 38 | static char text[4096]; | ||
| 39 | static int promptw = 0; | ||
| 40 | static int ret = 0; | ||
| 41 | static int screen; | ||
| 42 | static unsigned int mw, mh; | ||
| 43 | static unsigned int cursor = 0; | ||
| 44 | static unsigned int numlockmask = 0; | ||
| 45 | static Bool running = True; | ||
| 46 | static Display *dpy; | ||
| 47 | static Window parent, win; | ||
| 48 | |||
| 49 | #include "draw.c" | ||
| 50 | |||
| 51 | void | ||
| 52 | cleanup(void) { | ||
| 53 | dccleanup(); | ||
| 54 | XDestroyWindow(dpy, win); | ||
| 55 | XUngrabKeyboard(dpy, CurrentTime); | ||
| 56 | } | ||
| 57 | |||
| 58 | void | ||
| 59 | drawcursor(void) { | ||
| 60 | XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 }; | ||
| 61 | |||
| 62 | r.x += textnw(text, cursor) + dc.font.height / 2; | ||
| 63 | |||
| 64 | XSetForeground(dpy, dc.gc, dc.norm[ColFG]); | ||
| 65 | XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | ||
| 66 | } | ||
| 67 | |||
| 68 | void | ||
| 69 | drawinput(void) | ||
| 70 | { | ||
| 71 | dc.x = 0; | ||
| 72 | dc.y = 0; | ||
| 73 | dc.w = mw; | ||
| 74 | dc.h = mh; | ||
| 75 | drawtext(NULL, dc.norm); | ||
| 76 | /* print prompt? */ | ||
| 77 | if(prompt) { | ||
| 78 | dc.w = promptw; | ||
| 79 | drawtext(prompt, dc.sel); | ||
| 80 | dc.x += dc.w; | ||
| 81 | } | ||
| 82 | dc.w = mw - dc.x; | ||
| 83 | drawtext(*text ? text : NULL, dc.norm); | ||
| 84 | drawcursor(); | ||
| 85 | XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0); | ||
| 86 | XFlush(dpy); | ||
| 87 | } | ||
| 88 | |||
| 89 | void | ||
| 90 | eprint(const char *errstr, ...) { | ||
| 91 | va_list ap; | ||
| 92 | |||
| 93 | va_start(ap, errstr); | ||
| 94 | vfprintf(stderr, errstr, ap); | ||
| 95 | va_end(ap); | ||
| 96 | exit(EXIT_FAILURE); | ||
| 97 | } | ||
| 98 | |||
| 99 | Bool | ||
| 100 | grabkeyboard(void) { | ||
| 101 | unsigned int len; | ||
| 102 | |||
| 103 | for(len = 1000; len; len--) { | ||
| 104 | if(XGrabKeyboard(dpy, parent, True, GrabModeAsync, GrabModeAsync, CurrentTime) | ||
| 105 | == GrabSuccess) | ||
| 106 | break; | ||
| 107 | usleep(1000); | ||
| 108 | } | ||
| 109 | return len > 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | void | ||
| 113 | kpress(XKeyEvent * e) { | ||
| 114 | char buf[sizeof text]; | ||
| 115 | int num; | ||
| 116 | unsigned int i, len; | ||
| 117 | KeySym ksym; | ||
| 118 | |||
| 119 | len = strlen(text); | ||
| 120 | num = XLookupString(e, buf, sizeof buf, &ksym, NULL); | ||
| 121 | if(ksym == XK_KP_Enter) | ||
| 122 | ksym = XK_Return; | ||
| 123 | else if(ksym >= XK_KP_0 && ksym <= XK_KP_9) | ||
| 124 | ksym = (ksym - XK_KP_0) + XK_0; | ||
| 125 | else if(IsFunctionKey(ksym) || IsKeypadKey(ksym) | ||
| 126 | || IsMiscFunctionKey(ksym) || IsPFKey(ksym) | ||
| 127 | || IsPrivateKeypadKey(ksym)) | ||
| 128 | return; | ||
| 129 | /* first check if a control mask is omitted */ | ||
| 130 | if(e->state & ControlMask) { | ||
| 131 | switch(tolower(ksym)) { | ||
| 132 | default: | ||
| 133 | return; | ||
| 134 | case XK_a: | ||
| 135 | ksym = XK_Home; | ||
| 136 | break; | ||
| 137 | case XK_b: | ||
| 138 | ksym = XK_Left; | ||
| 139 | break; | ||
| 140 | case XK_c: | ||
| 141 | ksym = XK_Escape; | ||
| 142 | break; | ||
| 143 | case XK_e: | ||
| 144 | ksym = XK_End; | ||
| 145 | break; | ||
| 146 | case XK_f: | ||
| 147 | ksym = XK_Right; | ||
| 148 | break; | ||
| 149 | case XK_h: | ||
| 150 | ksym = XK_BackSpace; | ||
| 151 | break; | ||
| 152 | case XK_j: | ||
| 153 | ksym = XK_Return; | ||
| 154 | break; | ||
| 155 | case XK_k: | ||
| 156 | text[cursor] = '\0'; | ||
| 157 | break; | ||
| 158 | case XK_u: | ||
| 159 | memmove(text, text + cursor, sizeof text - cursor + 1); | ||
| 160 | cursor = 0; | ||
| 161 | break; | ||
| 162 | case XK_w: | ||
| 163 | if(cursor > 0) { | ||
| 164 | i = cursor; | ||
| 165 | while(i-- > 0 && text[i] == ' '); | ||
| 166 | while(i-- > 0 && text[i] != ' '); | ||
| 167 | memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); | ||
| 168 | cursor = i + 1; | ||
| 169 | } | ||
| 170 | break; | ||
| 171 | case XK_y: | ||
| 172 | { | ||
| 173 | FILE *fp; | ||
| 174 | char *s; | ||
| 175 | if(!(fp = popen("sselp", "r"))) | ||
| 176 | eprint("dinput: cannot popen sselp\n"); | ||
| 177 | s = fgets(buf, sizeof buf, fp); | ||
| 178 | pclose(fp); | ||
| 179 | if(s == NULL) | ||
| 180 | return; | ||
| 181 | } | ||
| 182 | num = strlen(buf); | ||
| 183 | if(num && buf[num-1] == '\n') | ||
| 184 | buf[--num] = '\0'; | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | switch(ksym) { | ||
| 189 | default: | ||
| 190 | num = MIN(num, sizeof text - cursor); | ||
| 191 | if(num && !iscntrl((int) buf[0])) { | ||
| 192 | memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); | ||
| 193 | memcpy(text + cursor, buf, num); | ||
| 194 | cursor += num; | ||
| 195 | } | ||
| 196 | break; | ||
| 197 | case XK_BackSpace: | ||
| 198 | if(cursor == 0) | ||
| 199 | return; | ||
| 200 | for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); | ||
| 201 | memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); | ||
| 202 | cursor -= i; | ||
| 203 | break; | ||
| 204 | case XK_Delete: | ||
| 205 | if(cursor == len) | ||
| 206 | return; | ||
| 207 | for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++); | ||
| 208 | memmove(text + cursor, text + cursor + i, sizeof text - cursor); | ||
| 209 | break; | ||
| 210 | case XK_End: | ||
| 211 | cursor = len; | ||
| 212 | break; | ||
| 213 | case XK_Escape: | ||
| 214 | ret = 1; | ||
| 215 | running = False; | ||
| 216 | return; | ||
| 217 | case XK_Home: | ||
| 218 | cursor = 0; | ||
| 219 | break; | ||
| 220 | case XK_Left: | ||
| 221 | if(cursor == 0) | ||
| 222 | return; | ||
| 223 | while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); | ||
| 224 | break; | ||
| 225 | case XK_Return: | ||
| 226 | fprintf(stdout, "%s", text); | ||
| 227 | fflush(stdout); | ||
| 228 | running = False; | ||
| 229 | return; | ||
| 230 | case XK_Right: | ||
| 231 | if(cursor == len) | ||
| 232 | return; | ||
| 233 | while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | drawinput(); | ||
| 237 | } | ||
| 238 | |||
| 239 | void | ||
| 240 | run(void) { | ||
| 241 | XEvent ev; | ||
| 242 | |||
| 243 | /* main event loop */ | ||
| 244 | while(running && !XNextEvent(dpy, &ev)) | ||
| 245 | switch (ev.type) { | ||
| 246 | case KeyPress: | ||
| 247 | kpress(&ev.xkey); | ||
| 248 | break; | ||
| 249 | case Expose: | ||
| 250 | if(ev.xexpose.count == 0) | ||
| 251 | drawinput(); | ||
| 252 | break; | ||
| 253 | case VisibilityNotify: | ||
| 254 | if (ev.xvisibility.state != VisibilityUnobscured) | ||
| 255 | XRaiseWindow(dpy, win); | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | void | ||
| 261 | setup(Bool topbar) { | ||
| 262 | int i, j, x, y; | ||
| 263 | #if XINERAMA | ||
| 264 | int n; | ||
| 265 | XineramaScreenInfo *info = NULL; | ||
| 266 | #endif | ||
| 267 | XModifierKeymap *modmap; | ||
| 268 | XSetWindowAttributes wa; | ||
| 269 | XWindowAttributes pwa; | ||
| 270 | |||
| 271 | /* init modifier map */ | ||
| 272 | modmap = XGetModifierMapping(dpy); | ||
| 273 | for(i = 0; i < 8; i++) | ||
| 274 | for(j = 0; j < modmap->max_keypermod; j++) { | ||
| 275 | if(modmap->modifiermap[i * modmap->max_keypermod + j] | ||
| 276 | == XKeysymToKeycode(dpy, XK_Num_Lock)) | ||
| 277 | numlockmask = (1 << i); | ||
| 278 | } | ||
| 279 | XFreeModifiermap(modmap); | ||
| 280 | |||
| 281 | /* style */ | ||
| 282 | dc.norm[ColBG] = getcolor(normbgcolor); | ||
| 283 | dc.norm[ColFG] = getcolor(normfgcolor); | ||
| 284 | dc.sel[ColBG] = getcolor(selbgcolor); | ||
| 285 | dc.sel[ColFG] = getcolor(selfgcolor); | ||
| 286 | initfont(font); | ||
| 287 | |||
| 288 | /* menu window */ | ||
| 289 | wa.override_redirect = True; | ||
| 290 | wa.background_pixmap = ParentRelative; | ||
| 291 | wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask; | ||
| 292 | |||
| 293 | /* menu window geometry */ | ||
| 294 | mh = (dc.font.height + 2); | ||
| 295 | #if XINERAMA | ||
| 296 | if(parent == RootWindow(dpy, screen) && XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { | ||
| 297 | i = 0; | ||
| 298 | if(n > 1) { | ||
| 299 | int di; | ||
| 300 | unsigned int dui; | ||
| 301 | Window dummy; | ||
| 302 | if(XQueryPointer(dpy, parent, &dummy, &dummy, &x, &y, &di, &di, &dui)) | ||
| 303 | for(i = 0; i < n; i++) | ||
| 304 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | ||
| 305 | break; | ||
| 306 | } | ||
| 307 | x = info[i].x_org; | ||
| 308 | y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh; | ||
| 309 | mw = info[i].width; | ||
| 310 | XFree(info); | ||
| 311 | } | ||
| 312 | else | ||
| 313 | #endif | ||
| 314 | { | ||
| 315 | XGetWindowAttributes(dpy, parent, &pwa); | ||
| 316 | x = 0; | ||
| 317 | y = topbar ? 0 : pwa.height - mh; | ||
| 318 | mw = pwa.width; | ||
| 319 | } | ||
| 320 | |||
| 321 | win = XCreateWindow(dpy, parent, x, y, mw, mh, 0, | ||
| 322 | DefaultDepth(dpy, screen), CopyFromParent, | ||
| 323 | DefaultVisual(dpy, screen), | ||
| 324 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||
| 325 | |||
| 326 | /* pixmap */ | ||
| 327 | dcsetup(); | ||
| 328 | if(prompt) | ||
| 329 | promptw = MIN(textw(prompt), mw / 5); | ||
| 330 | cursor = strlen(text); | ||
| 331 | XMapRaised(dpy, win); | ||
| 332 | } | ||
| 333 | |||
| 334 | int | ||
| 335 | main(int argc, char *argv[]) { | ||
| 336 | unsigned int i; | ||
| 337 | Bool topbar = True; | ||
| 338 | |||
| 339 | /* command line args */ | ||
| 340 | for(i = 1; i < argc; i++) | ||
| 341 | if(!strcmp(argv[i], "-b")) | ||
| 342 | topbar = False; | ||
| 343 | else if(!strcmp(argv[i], "-e")) { | ||
| 344 | if(++i < argc) parent = atoi(argv[i]); | ||
| 345 | } | ||
| 346 | else if(!strcmp(argv[i], "-fn")) { | ||
| 347 | if(++i < argc) font = argv[i]; | ||
| 348 | } | ||
| 349 | else if(!strcmp(argv[i], "-nb")) { | ||
| 350 | if(++i < argc) normbgcolor = argv[i]; | ||
| 351 | } | ||
| 352 | else if(!strcmp(argv[i], "-nf")) { | ||
| 353 | if(++i < argc) normfgcolor = argv[i]; | ||
| 354 | } | ||
| 355 | else if(!strcmp(argv[i], "-p")) { | ||
| 356 | if(++i < argc) prompt = argv[i]; | ||
| 357 | } | ||
| 358 | else if(!strcmp(argv[i], "-sb")) { | ||
| 359 | if(++i < argc) selbgcolor = argv[i]; | ||
| 360 | } | ||
| 361 | else if(!strcmp(argv[i], "-sf")) { | ||
| 362 | if(++i < argc) selfgcolor = argv[i]; | ||
| 363 | } | ||
| 364 | else if(!strcmp(argv[i], "-v")) | ||
| 365 | eprint("dinput-"VERSION", © 2006-2010 dinput engineers, see LICENSE for details\n"); | ||
| 366 | else if(!*text) | ||
| 367 | strncpy(text, argv[i], sizeof text); | ||
| 368 | else | ||
| 369 | eprint("usage: dinput [-b] [-e <xid>] [-fn <font>] [-nb <color>] [-nf <color>]\n" | ||
| 370 | " [-p <prompt>] [-sb <color>] [-sf <color>] [-v] [<text>]\n"); | ||
| 371 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
| 372 | fprintf(stderr, "dinput: warning: no locale support\n"); | ||
| 373 | if(!(dpy = XOpenDisplay(NULL))) | ||
| 374 | eprint("dinput: cannot open display\n"); | ||
| 375 | screen = DefaultScreen(dpy); | ||
| 376 | if(!parent) | ||
| 377 | parent = RootWindow(dpy, screen); | ||
| 378 | |||
| 379 | running = grabkeyboard(); | ||
| 380 | setup(topbar); | ||
| 381 | drawinput(); | ||
| 382 | XSync(dpy, False); | ||
| 383 | run(); | ||
| 384 | cleanup(); | ||
| 385 | XCloseDisplay(dpy); | ||
| 386 | return ret; | ||
| 387 | } | ||
| @@ -21,25 +21,6 @@ | |||
| 21 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | 21 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) |
| 22 | #define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) | 22 | #define IS_UTF8_1ST_CHAR(c) ((((c) & 0xc0) == 0xc0) || !((c) & 0x80)) |
| 23 | 23 | ||
| 24 | /* enums */ | ||
| 25 | enum { ColFG, ColBG, ColLast }; | ||
| 26 | |||
| 27 | /* typedefs */ | ||
| 28 | typedef struct { | ||
| 29 | int x, y, w, h; | ||
| 30 | unsigned long norm[ColLast]; | ||
| 31 | unsigned long sel[ColLast]; | ||
| 32 | Drawable drawable; | ||
| 33 | GC gc; | ||
| 34 | struct { | ||
| 35 | XFontStruct *xfont; | ||
| 36 | XFontSet set; | ||
| 37 | int ascent; | ||
| 38 | int descent; | ||
| 39 | int height; | ||
| 40 | } font; | ||
| 41 | } DC; /* draw context */ | ||
| 42 | |||
| 43 | typedef struct Item Item; | 24 | typedef struct Item Item; |
| 44 | struct Item { | 25 | struct Item { |
| 45 | char *text; | 26 | char *text; |
| @@ -53,22 +34,16 @@ static void calcoffsetsh(void); | |||
| 53 | static void calcoffsetsv(void); | 34 | static void calcoffsetsv(void); |
| 54 | static char *cistrstr(const char *s, const char *sub); | 35 | static char *cistrstr(const char *s, const char *sub); |
| 55 | static void cleanup(void); | 36 | static void cleanup(void); |
| 56 | static void drawcursor(void); | ||
| 57 | static void drawmenu(void); | 37 | static void drawmenu(void); |
| 58 | static void drawmenuh(void); | 38 | static void drawmenuh(void); |
| 59 | static void drawmenuv(void); | 39 | static void drawmenuv(void); |
| 60 | static void drawtext(const char *text, unsigned long col[ColLast]); | ||
| 61 | static void eprint(const char *errstr, ...); | 40 | static void eprint(const char *errstr, ...); |
| 62 | static unsigned long getcolor(const char *colstr); | ||
| 63 | static Bool grabkeyboard(void); | 41 | static Bool grabkeyboard(void); |
| 64 | static void initfont(const char *fontstr); | ||
| 65 | static void kpress(XKeyEvent * e); | 42 | static void kpress(XKeyEvent * e); |
| 66 | static void match(char *pattern); | 43 | static void match(char *pattern); |
| 67 | static void readstdin(void); | 44 | static void readstdin(void); |
| 68 | static void run(void); | 45 | static void run(void); |
| 69 | static void setup(Bool topbar); | 46 | static void setup(Bool topbar); |
| 70 | static int textnw(const char *text, unsigned int len); | ||
| 71 | static int textw(const char *text); | ||
| 72 | 47 | ||
| 73 | #include "config.h" | 48 | #include "config.h" |
| 74 | 49 | ||
| @@ -81,11 +56,9 @@ static int promptw = 0; | |||
| 81 | static int ret = 0; | 56 | static int ret = 0; |
| 82 | static int screen; | 57 | static int screen; |
| 83 | static unsigned int mw, mh; | 58 | static unsigned int mw, mh; |
| 84 | static unsigned int cursor = 0; | ||
| 85 | static unsigned int numlockmask = 0; | 59 | static unsigned int numlockmask = 0; |
| 86 | static Bool running = True; | 60 | static Bool running = True; |
| 87 | static Display *dpy; | 61 | static Display *dpy; |
| 88 | static DC dc; | ||
| 89 | static Item *allitems = NULL; /* first of all items */ | 62 | static Item *allitems = NULL; /* first of all items */ |
| 90 | static Item *item = NULL; /* first of pattern matching items */ | 63 | static Item *item = NULL; /* first of pattern matching items */ |
| 91 | static Item *sel = NULL; | 64 | static Item *sel = NULL; |
| @@ -98,6 +71,8 @@ static char *(*fstrstr)(const char *, const char *) = strstr; | |||
| 98 | static unsigned int lines = 0; | 71 | static unsigned int lines = 0; |
| 99 | static void (*calcoffsets)(void) = calcoffsetsh; | 72 | static void (*calcoffsets)(void) = calcoffsetsh; |
| 100 | 73 | ||
| 74 | #include "draw.c" | ||
| 75 | |||
| 101 | void | 76 | void |
| 102 | appenditem(Item *i, Item **list, Item **last) { | 77 | appenditem(Item *i, Item **list, Item **last) { |
| 103 | if(!(*last)) | 78 | if(!(*last)) |
| @@ -161,27 +136,12 @@ cistrstr(const char *s, const char *sub) { | |||
| 161 | 136 | ||
| 162 | void | 137 | void |
| 163 | cleanup(void) { | 138 | cleanup(void) { |
| 164 | if(dc.font.set) | 139 | dccleanup(); |
| 165 | XFreeFontSet(dpy, dc.font.set); | ||
| 166 | else | ||
| 167 | XFreeFont(dpy, dc.font.xfont); | ||
| 168 | XFreePixmap(dpy, dc.drawable); | ||
| 169 | XFreeGC(dpy, dc.gc); | ||
| 170 | XDestroyWindow(dpy, win); | 140 | XDestroyWindow(dpy, win); |
| 171 | XUngrabKeyboard(dpy, CurrentTime); | 141 | XUngrabKeyboard(dpy, CurrentTime); |
| 172 | } | 142 | } |
| 173 | 143 | ||
| 174 | void | 144 | void |
| 175 | drawcursor(void) { | ||
| 176 | XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 }; | ||
| 177 | |||
| 178 | r.x += textnw(text, cursor) + dc.font.height / 2; | ||
| 179 | |||
| 180 | XSetForeground(dpy, dc.gc, dc.norm[ColFG]); | ||
| 181 | XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | ||
| 182 | } | ||
| 183 | |||
| 184 | void | ||
| 185 | drawmenu(void) { | 145 | drawmenu(void) { |
| 186 | dc.x = 0; | 146 | dc.x = 0; |
| 187 | dc.y = 0; | 147 | dc.y = 0; |
| @@ -199,7 +159,6 @@ drawmenu(void) { | |||
| 199 | if(cmdw && item && lines == 0) | 159 | if(cmdw && item && lines == 0) |
| 200 | dc.w = cmdw; | 160 | dc.w = cmdw; |
| 201 | drawtext(*text ? text : NULL, dc.norm); | 161 | drawtext(*text ? text : NULL, dc.norm); |
| 202 | drawcursor(); | ||
| 203 | if(curr) { | 162 | if(curr) { |
| 204 | if(lines > 0) | 163 | if(lines > 0) |
| 205 | drawmenuv(); | 164 | drawmenuv(); |
| @@ -244,34 +203,6 @@ drawmenuv(void) { | |||
| 244 | } | 203 | } |
| 245 | 204 | ||
| 246 | void | 205 | void |
| 247 | drawtext(const char *text, unsigned long col[ColLast]) { | ||
| 248 | char buf[256]; | ||
| 249 | int i, x, y, h, len, olen; | ||
| 250 | XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | ||
| 251 | |||
| 252 | XSetForeground(dpy, dc.gc, col[ColBG]); | ||
| 253 | XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | ||
| 254 | if(!text) | ||
| 255 | return; | ||
| 256 | olen = strlen(text); | ||
| 257 | h = dc.font.height; | ||
| 258 | y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent; | ||
| 259 | x = dc.x + (h / 2); | ||
| 260 | /* shorten text if necessary */ | ||
| 261 | for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); | ||
| 262 | if(!len) | ||
| 263 | return; | ||
| 264 | memcpy(buf, text, len); | ||
| 265 | if(len < olen) | ||
| 266 | for(i = len; i && i > len - 3; buf[--i] = '.'); | ||
| 267 | XSetForeground(dpy, dc.gc, col[ColFG]); | ||
| 268 | if(dc.font.set) | ||
| 269 | XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); | ||
| 270 | else | ||
| 271 | XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); | ||
| 272 | } | ||
| 273 | |||
| 274 | void | ||
| 275 | eprint(const char *errstr, ...) { | 206 | eprint(const char *errstr, ...) { |
| 276 | va_list ap; | 207 | va_list ap; |
| 277 | 208 | ||
| @@ -281,16 +212,6 @@ eprint(const char *errstr, ...) { | |||
| 281 | exit(EXIT_FAILURE); | 212 | exit(EXIT_FAILURE); |
| 282 | } | 213 | } |
| 283 | 214 | ||
| 284 | unsigned long | ||
| 285 | getcolor(const char *colstr) { | ||
| 286 | Colormap cmap = DefaultColormap(dpy, screen); | ||
| 287 | XColor color; | ||
| 288 | |||
| 289 | if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) | ||
| 290 | eprint("dmenu: cannot allocate color '%s'\n", colstr); | ||
| 291 | return color.pixel; | ||
| 292 | } | ||
| 293 | |||
| 294 | Bool | 215 | Bool |
| 295 | grabkeyboard(void) { | 216 | grabkeyboard(void) { |
| 296 | unsigned int len; | 217 | unsigned int len; |
| @@ -305,37 +226,6 @@ grabkeyboard(void) { | |||
| 305 | } | 226 | } |
| 306 | 227 | ||
| 307 | void | 228 | void |
| 308 | initfont(const char *fontstr) { | ||
| 309 | char *def, **missing = NULL; | ||
| 310 | int i, n; | ||
| 311 | |||
| 312 | if(!fontstr || fontstr[0] == '\0') | ||
| 313 | eprint("dmenu: cannot load font: '%s'\n", fontstr); | ||
| 314 | dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); | ||
| 315 | if(missing) | ||
| 316 | XFreeStringList(missing); | ||
| 317 | if(dc.font.set) { | ||
| 318 | XFontStruct **xfonts; | ||
| 319 | char **font_names; | ||
| 320 | dc.font.ascent = dc.font.descent = 0; | ||
| 321 | n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); | ||
| 322 | for(i = 0; i < n; i++) { | ||
| 323 | dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent); | ||
| 324 | dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent); | ||
| 325 | xfonts++; | ||
| 326 | } | ||
| 327 | } | ||
| 328 | else { | ||
| 329 | if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) | ||
| 330 | && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) | ||
| 331 | eprint("dmenu: cannot load font: '%s'\n", fontstr); | ||
| 332 | dc.font.ascent = dc.font.xfont->ascent; | ||
| 333 | dc.font.descent = dc.font.xfont->descent; | ||
| 334 | } | ||
| 335 | dc.font.height = dc.font.ascent + dc.font.descent; | ||
| 336 | } | ||
| 337 | |||
| 338 | void | ||
| 339 | kpress(XKeyEvent * e) { | 229 | kpress(XKeyEvent * e) { |
| 340 | char buf[sizeof text]; | 230 | char buf[sizeof text]; |
| 341 | int num; | 231 | int num; |
| @@ -381,9 +271,6 @@ kpress(XKeyEvent * e) { | |||
| 381 | case XK_j: | 271 | case XK_j: |
| 382 | ksym = XK_Return; | 272 | ksym = XK_Return; |
| 383 | break; | 273 | break; |
| 384 | case XK_k: | ||
| 385 | text[cursor] = '\0'; | ||
| 386 | break; | ||
| 387 | case XK_n: | 274 | case XK_n: |
| 388 | ksym = XK_Down; | 275 | ksym = XK_Down; |
| 389 | break; | 276 | break; |
| @@ -391,67 +278,42 @@ kpress(XKeyEvent * e) { | |||
| 391 | ksym = XK_Up; | 278 | ksym = XK_Up; |
| 392 | break; | 279 | break; |
| 393 | case XK_u: | 280 | case XK_u: |
| 394 | memmove(text, text + cursor, sizeof text - cursor + 1); | 281 | text[0] = '\0'; |
| 395 | cursor = 0; | ||
| 396 | match(text); | 282 | match(text); |
| 397 | break; | 283 | break; |
| 398 | case XK_w: | 284 | case XK_w: |
| 399 | if(cursor > 0) { | 285 | if(len == 0) |
| 400 | i = cursor; | 286 | return; |
| 401 | while(i-- > 0 && text[i] == ' '); | 287 | i = len; |
| 402 | while(i-- > 0 && text[i] != ' '); | 288 | while(i-- > 0 && text[i] == ' '); |
| 403 | memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); | 289 | while(i-- > 0 && text[i] != ' '); |
| 404 | cursor = i + 1; | 290 | text[++i] = '\0'; |
| 405 | match(text); | 291 | match(text); |
| 406 | } | ||
| 407 | break; | 292 | break; |
| 408 | case XK_y: | 293 | case XK_x: |
| 409 | { | 294 | execlp("dinput", "dinput", text, NULL); /* todo: argv */ |
| 410 | FILE *fp; | 295 | eprint("dmenu: cannot exec dinput:"); |
| 411 | char *s; | ||
| 412 | if(!(fp = popen("sselp", "r"))) | ||
| 413 | eprint("dmenu: cannot popen sselp\n"); | ||
| 414 | s = fgets(buf, sizeof buf, fp); | ||
| 415 | pclose(fp); | ||
| 416 | if(s == NULL) | ||
| 417 | return; | ||
| 418 | } | ||
| 419 | num = strlen(buf); | ||
| 420 | if(num && buf[num-1] == '\n') | ||
| 421 | buf[--num] = '\0'; | ||
| 422 | break; | 296 | break; |
| 423 | } | 297 | } |
| 424 | } | 298 | } |
| 425 | switch(ksym) { | 299 | switch(ksym) { |
| 426 | default: | 300 | default: |
| 427 | num = MIN(num, sizeof text - cursor); | 301 | num = MIN(num, sizeof text); |
| 428 | if(num && !iscntrl((int) buf[0])) { | 302 | if(num && !iscntrl((int) buf[0])) { |
| 429 | memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); | 303 | memcpy(text + len, buf, num + 1); |
| 430 | memcpy(text + cursor, buf, num); | 304 | len += num; |
| 431 | cursor += num; | ||
| 432 | match(text); | 305 | match(text); |
| 433 | } | 306 | } |
| 434 | break; | 307 | break; |
| 435 | case XK_BackSpace: | 308 | case XK_BackSpace: |
| 436 | if(cursor == 0) | 309 | if(len == 0) |
| 437 | return; | 310 | return; |
| 438 | for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); | 311 | for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++); |
| 439 | memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); | 312 | len -= i; |
| 440 | cursor -= i; | 313 | text[len] = '\0'; |
| 441 | match(text); | ||
| 442 | break; | ||
| 443 | case XK_Delete: | ||
| 444 | if(cursor == len) | ||
| 445 | return; | ||
| 446 | for(i = 1; cursor + i < len && !IS_UTF8_1ST_CHAR(text[cursor + i]); i++); | ||
| 447 | memmove(text + cursor, text + cursor + i, sizeof text - cursor); | ||
| 448 | match(text); | 314 | match(text); |
| 449 | break; | 315 | break; |
| 450 | case XK_End: | 316 | case XK_End: |
| 451 | if(cursor < len) { | ||
| 452 | cursor = len; | ||
| 453 | break; | ||
| 454 | } | ||
| 455 | while(next) { | 317 | while(next) { |
| 456 | sel = curr = next; | 318 | sel = curr = next; |
| 457 | calcoffsets(); | 319 | calcoffsets(); |
| @@ -464,20 +326,10 @@ kpress(XKeyEvent * e) { | |||
| 464 | running = False; | 326 | running = False; |
| 465 | return; | 327 | return; |
| 466 | case XK_Home: | 328 | case XK_Home: |
| 467 | if(sel == item) { | ||
| 468 | cursor = 0; | ||
| 469 | break; | ||
| 470 | } | ||
| 471 | sel = curr = item; | 329 | sel = curr = item; |
| 472 | calcoffsets(); | 330 | calcoffsets(); |
| 473 | break; | 331 | break; |
| 474 | case XK_Left: | 332 | case XK_Left: |
| 475 | if(cursor > 0 && (!sel || !sel->left || lines > 0)) { | ||
| 476 | while(cursor-- > 0 && !IS_UTF8_1ST_CHAR(text[cursor])); | ||
| 477 | break; | ||
| 478 | } | ||
| 479 | if(lines > 0) | ||
| 480 | return; | ||
| 481 | case XK_Up: | 333 | case XK_Up: |
| 482 | if(!sel || !sel->left) | 334 | if(!sel || !sel->left) |
| 483 | return; | 335 | return; |
| @@ -508,12 +360,6 @@ kpress(XKeyEvent * e) { | |||
| 508 | running = False; | 360 | running = False; |
| 509 | return; | 361 | return; |
| 510 | case XK_Right: | 362 | case XK_Right: |
| 511 | if(cursor < len) { | ||
| 512 | while(cursor++ < len && !IS_UTF8_1ST_CHAR(text[cursor])); | ||
| 513 | break; | ||
| 514 | } | ||
| 515 | if(lines > 0) | ||
| 516 | return; | ||
| 517 | case XK_Down: | 363 | case XK_Down: |
| 518 | if(!sel || !sel->right) | 364 | if(!sel || !sel->right) |
| 519 | return; | 365 | return; |
| @@ -527,7 +373,6 @@ kpress(XKeyEvent * e) { | |||
| 527 | if(!sel) | 373 | if(!sel) |
| 528 | return; | 374 | return; |
| 529 | strncpy(text, sel->text, sizeof text); | 375 | strncpy(text, sel->text, sizeof text); |
| 530 | cursor = strlen(text); | ||
| 531 | match(text); | 376 | match(text); |
| 532 | break; | 377 | break; |
| 533 | } | 378 | } |
| @@ -690,11 +535,7 @@ setup(Bool topbar) { | |||
| 690 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | 535 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
| 691 | 536 | ||
| 692 | /* pixmap */ | 537 | /* pixmap */ |
| 693 | dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); | 538 | dcsetup(); |
| 694 | dc.gc = XCreateGC(dpy, parent, 0, NULL); | ||
| 695 | XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); | ||
| 696 | if(!dc.font.set) | ||
| 697 | XSetFont(dpy, dc.gc, dc.font.xfont->fid); | ||
| 698 | if(maxname) | 539 | if(maxname) |
| 699 | cmdw = MIN(textw(maxname), mw / 3); | 540 | cmdw = MIN(textw(maxname), mw / 3); |
| 700 | if(prompt) | 541 | if(prompt) |
| @@ -705,22 +546,6 @@ setup(Bool topbar) { | |||
| 705 | } | 546 | } |
| 706 | 547 | ||
| 707 | int | 548 | int |
| 708 | textnw(const char *text, unsigned int len) { | ||
| 709 | XRectangle r; | ||
| 710 | |||
| 711 | if(dc.font.set) { | ||
| 712 | XmbTextExtents(dc.font.set, text, len, NULL, &r); | ||
| 713 | return r.width; | ||
| 714 | } | ||
| 715 | return XTextWidth(dc.font.xfont, text, len); | ||
| 716 | } | ||
| 717 | |||
| 718 | int | ||
| 719 | textw(const char *text) { | ||
| 720 | return textnw(text, strlen(text)) + dc.font.height; | ||
| 721 | } | ||
| 722 | |||
| 723 | int | ||
| 724 | main(int argc, char *argv[]) { | 549 | main(int argc, char *argv[]) { |
| 725 | unsigned int i; | 550 | unsigned int i; |
| 726 | Bool topbar = True; | 551 | Bool topbar = True; |
| @@ -0,0 +1,143 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | /* enums */ | ||
| 4 | enum { ColFG, ColBG, ColLast }; | ||
| 5 | |||
| 6 | /* typedefs */ | ||
| 7 | typedef struct { | ||
| 8 | int x, y, w, h; | ||
| 9 | unsigned long norm[ColLast]; | ||
| 10 | unsigned long sel[ColLast]; | ||
| 11 | Drawable drawable; | ||
| 12 | GC gc; | ||
| 13 | struct { | ||
| 14 | XFontStruct *xfont; | ||
| 15 | XFontSet set; | ||
| 16 | int ascent; | ||
| 17 | int descent; | ||
| 18 | int height; | ||
| 19 | } font; | ||
| 20 | } DC; /* draw context */ | ||
| 21 | |||
| 22 | /* forward declarations */ | ||
| 23 | static void dccleanup(void); | ||
| 24 | static void dcsetup(void); | ||
| 25 | static void drawtext(const char *text, unsigned long col[ColLast]); | ||
| 26 | static unsigned long getcolor(const char *colstr); | ||
| 27 | static void initfont(const char *fontstr); | ||
| 28 | static int textnw(const char *text, unsigned int len); | ||
| 29 | static int textw(const char *text); | ||
| 30 | |||
| 31 | static DC dc; | ||
| 32 | |||
| 33 | void | ||
| 34 | dccleanup(void) { | ||
| 35 | if(dc.font.set) | ||
| 36 | XFreeFontSet(dpy, dc.font.set); | ||
| 37 | else | ||
| 38 | XFreeFont(dpy, dc.font.xfont); | ||
| 39 | XFreePixmap(dpy, dc.drawable); | ||
| 40 | XFreeGC(dpy, dc.gc); | ||
| 41 | } | ||
| 42 | |||
| 43 | void | ||
| 44 | dcsetup() { | ||
| 45 | /* style */ | ||
| 46 | dc.norm[ColBG] = getcolor(normbgcolor); | ||
| 47 | dc.norm[ColFG] = getcolor(normfgcolor); | ||
| 48 | dc.sel[ColBG] = getcolor(selbgcolor); | ||
| 49 | dc.sel[ColFG] = getcolor(selfgcolor); | ||
| 50 | initfont(font); | ||
| 51 | |||
| 52 | /* pixmap */ | ||
| 53 | dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); | ||
| 54 | dc.gc = XCreateGC(dpy, parent, 0, NULL); | ||
| 55 | XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); | ||
| 56 | if(!dc.font.set) | ||
| 57 | XSetFont(dpy, dc.gc, dc.font.xfont->fid); | ||
| 58 | } | ||
| 59 | |||
| 60 | void | ||
| 61 | drawtext(const char *text, unsigned long col[ColLast]) { | ||
| 62 | char buf[256]; | ||
| 63 | int i, x, y, h, len, olen; | ||
| 64 | XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | ||
| 65 | |||
| 66 | XSetForeground(dpy, dc.gc, col[ColBG]); | ||
| 67 | XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | ||
| 68 | if(!text) | ||
| 69 | return; | ||
| 70 | olen = strlen(text); | ||
| 71 | h = dc.font.height; | ||
| 72 | y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent; | ||
| 73 | x = dc.x + (h / 2); | ||
| 74 | /* shorten text if necessary */ | ||
| 75 | for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); | ||
| 76 | if(!len) | ||
| 77 | return; | ||
| 78 | memcpy(buf, text, len); | ||
| 79 | if(len < olen) | ||
| 80 | for(i = len; i && i > len - 3; buf[--i] = '.'); | ||
| 81 | XSetForeground(dpy, dc.gc, col[ColFG]); | ||
| 82 | if(dc.font.set) | ||
| 83 | XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); | ||
| 84 | else | ||
| 85 | XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); | ||
| 86 | } | ||
| 87 | |||
| 88 | unsigned long | ||
| 89 | getcolor(const char *colstr) { | ||
| 90 | Colormap cmap = DefaultColormap(dpy, screen); | ||
| 91 | XColor color; | ||
| 92 | |||
| 93 | if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) | ||
| 94 | eprint("drawtext: cannot allocate color '%s'\n", colstr); | ||
| 95 | return color.pixel; | ||
| 96 | } | ||
| 97 | |||
| 98 | void | ||
| 99 | initfont(const char *fontstr) { | ||
| 100 | char *def, **missing = NULL; | ||
| 101 | int i, n; | ||
| 102 | |||
| 103 | if(!fontstr || fontstr[0] == '\0') | ||
| 104 | eprint("drawtext: cannot load font: '%s'\n", fontstr); | ||
| 105 | dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); | ||
| 106 | if(missing) | ||
| 107 | XFreeStringList(missing); | ||
| 108 | if(dc.font.set) { | ||
| 109 | XFontStruct **xfonts; | ||
| 110 | char **font_names; | ||
| 111 | dc.font.ascent = dc.font.descent = 0; | ||
| 112 | n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); | ||
| 113 | for(i = 0; i < n; i++) { | ||
| 114 | dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent); | ||
| 115 | dc.font.descent = MAX(dc.font.descent, (*xfonts)->descent); | ||
| 116 | xfonts++; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | else { | ||
| 120 | if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) | ||
| 121 | && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) | ||
| 122 | eprint("drawtext: cannot load font: '%s'\n", fontstr); | ||
| 123 | dc.font.ascent = dc.font.xfont->ascent; | ||
| 124 | dc.font.descent = dc.font.xfont->descent; | ||
| 125 | } | ||
| 126 | dc.font.height = dc.font.ascent + dc.font.descent; | ||
| 127 | } | ||
| 128 | |||
| 129 | int | ||
| 130 | textnw(const char *text, unsigned int len) { | ||
| 131 | XRectangle r; | ||
| 132 | |||
| 133 | if(dc.font.set) { | ||
| 134 | XmbTextExtents(dc.font.set, text, len, NULL, &r); | ||
| 135 | return r.width; | ||
| 136 | } | ||
| 137 | return XTextWidth(dc.font.xfont, text, len); | ||
| 138 | } | ||
| 139 | |||
| 140 | int | ||
| 141 | textw(const char *text) { | ||
| 142 | return textnw(text, strlen(text)) + dc.font.height; | ||
| 143 | } | ||
