initial dmenu / dinput separation
b4e63454
4 file(s) · +561 −205
| 3 | 3 | ||
| 4 | 4 | include config.mk |
|
| 5 | 5 | ||
| 6 | - | SRC = dmenu.c |
|
| 6 | + | SRC = dinput.c dmenu.c draw.c |
|
| 7 | 7 | OBJ = ${SRC:.c=.o} |
|
| 8 | 8 | ||
| 9 | - | all: options dmenu |
|
| 9 | + | all: options dinput dmenu |
|
| 10 | 10 | ||
| 11 | 11 | options: |
|
| 12 | 12 | @echo dmenu build options: |
|
| 18 | 18 | @echo CC $< |
|
| 19 | 19 | @${CC} -c ${CFLAGS} $< |
|
| 20 | 20 | ||
| 21 | - | ${OBJ}: config.h config.mk |
|
| 21 | + | ${OBJ}: config.h config.mk draw.c |
|
| 22 | 22 | ||
| 23 | 23 | config.h: |
|
| 24 | 24 | @echo creating $@ from config.def.h |
|
| 25 | 25 | @cp config.def.h $@ |
|
| 26 | 26 | ||
| 27 | - | dmenu: ${OBJ} |
|
| 27 | + | .o: |
|
| 28 | 28 | @echo CC -o $@ |
|
| 29 | - | @${CC} -o $@ ${OBJ} ${LDFLAGS} |
|
| 29 | + | @${CC} -o $@ $< ${LDFLAGS} |
|
| 30 | 30 | ||
| 31 | 31 | clean: |
|
| 32 | 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 | 35 | dist: clean |
|
| 36 | 36 | @echo creating dist tarball |
|
| 43 | 43 | install: all |
|
| 44 | 44 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin |
|
| 45 | 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 | 48 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu |
|
| 48 | 49 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path |
|
| 49 | 50 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run |
|
| 55 | 56 | uninstall: |
|
| 56 | 57 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin |
|
| 57 | 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 | 60 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 |
|
| 60 | 61 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 |
|
| 61 | 62 | ||
| 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 | 21 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) |
|
| 22 | 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 | 24 | typedef struct Item Item; |
|
| 44 | 25 | struct Item { |
|
| 45 | 26 | char *text; |
|
| 53 | 34 | static void calcoffsetsv(void); |
|
| 54 | 35 | static char *cistrstr(const char *s, const char *sub); |
|
| 55 | 36 | static void cleanup(void); |
|
| 56 | - | static void drawcursor(void); |
|
| 57 | 37 | static void drawmenu(void); |
|
| 58 | 38 | static void drawmenuh(void); |
|
| 59 | 39 | static void drawmenuv(void); |
|
| 60 | - | static void drawtext(const char *text, unsigned long col[ColLast]); |
|
| 61 | 40 | static void eprint(const char *errstr, ...); |
|
| 62 | - | static unsigned long getcolor(const char *colstr); |
|
| 63 | 41 | static Bool grabkeyboard(void); |
|
| 64 | - | static void initfont(const char *fontstr); |
|
| 65 | 42 | static void kpress(XKeyEvent * e); |
|
| 66 | 43 | static void match(char *pattern); |
|
| 67 | 44 | static void readstdin(void); |
|
| 68 | 45 | static void run(void); |
|
| 69 | 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 | 48 | #include "config.h" |
|
| 74 | 49 | ||
| 81 | 56 | static int ret = 0; |
|
| 82 | 57 | static int screen; |
|
| 83 | 58 | static unsigned int mw, mh; |
|
| 84 | - | static unsigned int cursor = 0; |
|
| 85 | 59 | static unsigned int numlockmask = 0; |
|
| 86 | 60 | static Bool running = True; |
|
| 87 | 61 | static Display *dpy; |
|
| 88 | - | static DC dc; |
|
| 89 | 62 | static Item *allitems = NULL; /* first of all items */ |
|
| 90 | 63 | static Item *item = NULL; /* first of pattern matching items */ |
|
| 91 | 64 | static Item *sel = NULL; |
|
| 98 | 71 | static unsigned int lines = 0; |
|
| 99 | 72 | static void (*calcoffsets)(void) = calcoffsetsh; |
|
| 100 | 73 | ||
| 74 | + | #include "draw.c" |
|
| 75 | + | ||
| 101 | 76 | void |
|
| 102 | 77 | appenditem(Item *i, Item **list, Item **last) { |
|
| 103 | 78 | if(!(*last)) |
|
| 161 | 136 | ||
| 162 | 137 | void |
|
| 163 | 138 | cleanup(void) { |
|
| 164 | - | if(dc.font.set) |
|
| 165 | - | XFreeFontSet(dpy, dc.font.set); |
|
| 166 | - | else |
|
| 167 | - | XFreeFont(dpy, dc.font.xfont); |
|
| 168 | - | XFreePixmap(dpy, dc.drawable); |
|
| 169 | - | XFreeGC(dpy, dc.gc); |
|
| 139 | + | dccleanup(); |
|
| 170 | 140 | XDestroyWindow(dpy, win); |
|
| 171 | 141 | XUngrabKeyboard(dpy, CurrentTime); |
|
| 172 | 142 | } |
|
| 173 | 143 | ||
| 174 | 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 | 145 | drawmenu(void) { |
|
| 186 | 146 | dc.x = 0; |
|
| 187 | 147 | dc.y = 0; |
|
| 199 | 159 | if(cmdw && item && lines == 0) |
|
| 200 | 160 | dc.w = cmdw; |
|
| 201 | 161 | drawtext(*text ? text : NULL, dc.norm); |
|
| 202 | - | drawcursor(); |
|
| 203 | 162 | if(curr) { |
|
| 204 | 163 | if(lines > 0) |
|
| 205 | 164 | drawmenuv(); |
|
| 244 | 203 | } |
|
| 245 | 204 | ||
| 246 | 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 | 206 | eprint(const char *errstr, ...) { |
|
| 276 | 207 | va_list ap; |
|
| 277 | 208 | ||
| 281 | 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 | 215 | Bool |
|
| 295 | 216 | grabkeyboard(void) { |
|
| 296 | 217 | unsigned int len; |
|
| 305 | 226 | } |
|
| 306 | 227 | ||
| 307 | 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 | 229 | kpress(XKeyEvent * e) { |
|
| 340 | 230 | char buf[sizeof text]; |
|
| 341 | 231 | int num; |
|
| 381 | 271 | case XK_j: |
|
| 382 | 272 | ksym = XK_Return; |
|
| 383 | 273 | break; |
|
| 384 | - | case XK_k: |
|
| 385 | - | text[cursor] = '\0'; |
|
| 386 | - | break; |
|
| 387 | 274 | case XK_n: |
|
| 388 | 275 | ksym = XK_Down; |
|
| 389 | 276 | break; |
|
| 391 | 278 | ksym = XK_Up; |
|
| 392 | 279 | break; |
|
| 393 | 280 | case XK_u: |
|
| 394 | - | memmove(text, text + cursor, sizeof text - cursor + 1); |
|
| 395 | - | cursor = 0; |
|
| 281 | + | text[0] = '\0'; |
|
| 396 | 282 | match(text); |
|
| 397 | 283 | break; |
|
| 398 | 284 | case XK_w: |
|
| 399 | - | if(cursor > 0) { |
|
| 400 | - | i = cursor; |
|
| 401 | - | while(i-- > 0 && text[i] == ' '); |
|
| 402 | - | while(i-- > 0 && text[i] != ' '); |
|
| 403 | - | memmove(text + i + 1, text + cursor, sizeof text - cursor + 1); |
|
| 404 | - | cursor = i + 1; |
|
| 405 | - | match(text); |
|
| 406 | - | } |
|
| 285 | + | if(len == 0) |
|
| 286 | + | return; |
|
| 287 | + | i = len; |
|
| 288 | + | while(i-- > 0 && text[i] == ' '); |
|
| 289 | + | while(i-- > 0 && text[i] != ' '); |
|
| 290 | + | text[++i] = '\0'; |
|
| 291 | + | match(text); |
|
| 407 | 292 | break; |
|
| 408 | - | case XK_y: |
|
| 409 | - | { |
|
| 410 | - | FILE *fp; |
|
| 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'; |
|
| 293 | + | case XK_x: |
|
| 294 | + | execlp("dinput", "dinput", text, NULL); /* todo: argv */ |
|
| 295 | + | eprint("dmenu: cannot exec dinput:"); |
|
| 422 | 296 | break; |
|
| 423 | 297 | } |
|
| 424 | 298 | } |
|
| 425 | 299 | switch(ksym) { |
|
| 426 | 300 | default: |
|
| 427 | - | num = MIN(num, sizeof text - cursor); |
|
| 301 | + | num = MIN(num, sizeof text); |
|
| 428 | 302 | if(num && !iscntrl((int) buf[0])) { |
|
| 429 | - | memmove(text + cursor + num, text + cursor, sizeof text - cursor - num); |
|
| 430 | - | memcpy(text + cursor, buf, num); |
|
| 431 | - | cursor += num; |
|
| 303 | + | memcpy(text + len, buf, num + 1); |
|
| 304 | + | len += num; |
|
| 432 | 305 | match(text); |
|
| 433 | 306 | } |
|
| 434 | 307 | break; |
|
| 435 | 308 | case XK_BackSpace: |
|
| 436 | - | if(cursor == 0) |
|
| 309 | + | if(len == 0) |
|
| 437 | 310 | return; |
|
| 438 | - | for(i = 1; cursor - i > 0 && !IS_UTF8_1ST_CHAR(text[cursor - i]); i++); |
|
| 439 | - | memmove(text + cursor - i, text + cursor, sizeof text - cursor + i); |
|
| 440 | - | cursor -= i; |
|
| 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); |
|
| 311 | + | for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[len - i]); i++); |
|
| 312 | + | len -= i; |
|
| 313 | + | text[len] = '\0'; |
|
| 448 | 314 | match(text); |
|
| 449 | 315 | break; |
|
| 450 | 316 | case XK_End: |
|
| 451 | - | if(cursor < len) { |
|
| 452 | - | cursor = len; |
|
| 453 | - | break; |
|
| 454 | - | } |
|
| 455 | 317 | while(next) { |
|
| 456 | 318 | sel = curr = next; |
|
| 457 | 319 | calcoffsets(); |
|
| 464 | 326 | running = False; |
|
| 465 | 327 | return; |
|
| 466 | 328 | case XK_Home: |
|
| 467 | - | if(sel == item) { |
|
| 468 | - | cursor = 0; |
|
| 469 | - | break; |
|
| 470 | - | } |
|
| 471 | 329 | sel = curr = item; |
|
| 472 | 330 | calcoffsets(); |
|
| 473 | 331 | break; |
|
| 474 | 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 | 333 | case XK_Up: |
|
| 482 | 334 | if(!sel || !sel->left) |
|
| 483 | 335 | return; |
|
| 508 | 360 | running = False; |
|
| 509 | 361 | return; |
|
| 510 | 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 | 363 | case XK_Down: |
|
| 518 | 364 | if(!sel || !sel->right) |
|
| 519 | 365 | return; |
|
| 527 | 373 | if(!sel) |
|
| 528 | 374 | return; |
|
| 529 | 375 | strncpy(text, sel->text, sizeof text); |
|
| 530 | - | cursor = strlen(text); |
|
| 531 | 376 | match(text); |
|
| 532 | 377 | break; |
|
| 533 | 378 | } |
|
| 690 | 535 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
|
| 691 | 536 | ||
| 692 | 537 | /* pixmap */ |
|
| 693 | - | dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); |
|
| 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); |
|
| 538 | + | dcsetup(); |
|
| 698 | 539 | if(maxname) |
|
| 699 | 540 | cmdw = MIN(textw(maxname), mw / 3); |
|
| 700 | 541 | if(prompt) |
|
| 702 | 543 | text[0] = '\0'; |
|
| 703 | 544 | match(text); |
|
| 704 | 545 | XMapRaised(dpy, win); |
|
| 705 | - | } |
|
| 706 | - | ||
| 707 | - | 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 | 546 | } |
|
| 722 | 547 | ||
| 723 | 548 | int |
|
| 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 | + | } |