├── LICENSE ├── Makefile ├── README.md ├── config.def.h ├── config.mk ├── drw.c ├── drw.h ├── examples ├── brtwjt └── volwjt ├── util.c ├── util.h ├── wjt.1 └── wjt.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2006-2014 Anselm R Garbe 4 | © 2010-2012 Connor Lane Smith 5 | © 2009 Gottox 6 | © 2009 Markus Schnalke 7 | © 2009 Evan Gates 8 | © 2006-2008 Sander van Dijk 9 | © 2006-2007 Michał Janeczek 10 | © 2014-2015 Hiltjo Posthuma 11 | © 2016 Ian Remmler 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a 14 | copy of this software and associated documentation files (the "Software"), 15 | to deal in the Software without restriction, including without limitation 16 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 17 | and/or sell copies of the Software, and to permit persons to whom the 18 | Software is furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 26 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | DEALINGS IN THE SOFTWARE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # wjt - dynamic menu 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = drw.c wjt.c util.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options wjt 10 | 11 | options: 12 | @echo wjt build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | @echo CC $< 19 | @${CC} -c ${CFLAGS} $< 20 | 21 | config.h: 22 | @echo creating $@ from config.def.h 23 | @cp config.def.h $@ 24 | 25 | ${OBJ}: config.h config.mk drw.h 26 | 27 | wjt: wjt.o drw.o util.o 28 | @echo CC -o $@ 29 | @${CC} -o $@ wjt.o drw.o util.o ${LDFLAGS} 30 | 31 | clean: 32 | @echo cleaning 33 | @rm -f wjt ${OBJ} wjt-${VERSION}.tar.gz 34 | 35 | dist: clean 36 | @echo creating dist tarball 37 | @mkdir -p wjt-${VERSION} 38 | @cp LICENSE Makefile README arg.h config.def.h config.mk wjt.1 \ 39 | drw.h util.h ${SRC} \ 40 | wjt-${VERSION} 41 | @tar -cf wjt-${VERSION}.tar wjt-${VERSION} 42 | @gzip wjt-${VERSION}.tar 43 | @rm -rf wjt-${VERSION} 44 | 45 | install: all 46 | @echo installing executables to ${DESTDIR}${PREFIX}/bin 47 | @mkdir -p ${DESTDIR}${PREFIX}/bin 48 | @cp -f wjt ${DESTDIR}${PREFIX}/bin 49 | @chmod 755 ${DESTDIR}${PREFIX}/bin/wjt 50 | @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 51 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 52 | @sed "s/VERSION/${VERSION}/g" < wjt.1 > ${DESTDIR}${MANPREFIX}/man1/wjt.1 53 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/wjt.1 54 | 55 | uninstall: 56 | @echo removing executables from ${DESTDIR}${PREFIX}/bin 57 | @rm -f ${DESTDIR}${PREFIX}/bin/wjt 58 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 59 | @rm -f ${DESTDIR}${MANPREFIX}/man1/wjt.1 60 | 61 | .PHONY: all options clean dist install uninstall 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## wjt 2 | 3 | wjt is a slider widget for the X Window System that allows the user to select 4 | values within a range using the keyboard or mouse. It presents a bar at the 5 | top or bottom of the display with a slider area including legend labels, and 6 | optionally, a prompt on the left. When the user adjusts the slider position, 7 | its value is printed to stdout. wjt grabs keyboard and mouse input while 8 | running. wjt was inspired by, and its code is based on, dmenu. 9 | 10 | ### Dependencies 11 | 12 | - Xlib 13 | - FreeType 14 | - Fontconfig 15 | - Xinerama (optional) 16 | 17 | ### Configuration 18 | 19 | Change the default values in config.h and rebuild to customize wjt. Defaults 20 | may be overridden by command line options. 21 | 22 | ### Installation 23 | 24 | Edit config.mk to match your environment. The default install location is 25 | /usr/local. To build and install wjt, run: 26 | 27 | `> make clean install` 28 | 29 | ### Running wjt 30 | 31 | Running wjt with no options will put a slider at the top of the display with 32 | initial value 0 and a range of 0 to 100. See the man page for details on 33 | available options. 34 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | /* Default settings; can be overriden by command line. */ 3 | 4 | static int topbar = 1; /* -b: whether bar is at top or bottom of screen */ 5 | static int min = 0; /* -l: lower bound */ 6 | static int max = 100; /* -u: upper bound */ 7 | static int initval = 0; /* -x: initial value */ 8 | static int labelval = 1; /* -lv: whether to display value label */ 9 | static int labelexts = 1; /* -le: whether to display extent labels */ 10 | static int step = 1; /* -s: minimum adjustment */ 11 | static int jump = 10; /* -j: large adjustment */ 12 | static const char *prompt = NULL; /* -p: prompt to the left of slider */ 13 | static const char *special = NULL; /* -z: special text */ 14 | static const char *fonts[] = { /* -f: font set */ 15 | "monospace:size=10" 16 | }; 17 | 18 | static const char *colors[SchemeLast][2] = { 19 | /* fg bg */ 20 | [SchemePrompt] = { "#eeeeee", "#005577" }, 21 | [SchemeSlider] = { "#eeeeee", "#222222" }, /* fg: extent labels, bg: slider beyond value */ 22 | [SchemeValue] = { "#eeeeee", "#005577" }, /* fg: value label, bg: slider up to value */ 23 | }; 24 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # wjt version 2 | VERSION = 0.2 3 | 4 | # paths 5 | PREFIX = /usr/local 6 | MANPREFIX = ${PREFIX}/share/man 7 | 8 | X11INC = /usr/X11R6/include 9 | X11LIB = /usr/X11R6/lib 10 | 11 | # Xinerama, comment if you don't want it 12 | XINERAMALIBS = -lXinerama 13 | XINERAMAFLAGS = -DXINERAMA 14 | 15 | # freetype 16 | FREETYPELIBS = -lfontconfig -lXft 17 | FREETYPEINC = /usr/include/freetype2 18 | # OpenBSD (uncomment) 19 | #FREETYPEINC = ${X11INC}/freetype2 20 | 21 | # includes and libs 22 | INCS = -I${X11INC} -I${FREETYPEINC} 23 | LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} 24 | 25 | # flags 26 | CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 27 | CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} 28 | LDFLAGS = -s ${LIBS} 29 | 30 | # compiler and linker 31 | CC = cc 32 | -------------------------------------------------------------------------------- /drw.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 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 | { 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) 24 | return (unsigned char)c & ~utfmask[*i]; 25 | return 0; 26 | } 27 | 28 | static size_t 29 | utf8validate(long *u, size_t i) 30 | { 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) 32 | *u = UTF_INVALID; 33 | for (i = 1; *u > utfmax[i]; ++i) 34 | ; 35 | return i; 36 | } 37 | 38 | static size_t 39 | utf8decode(const char *c, long *u, size_t clen) 40 | { 41 | size_t i, j, len, type; 42 | long udecoded; 43 | 44 | *u = UTF_INVALID; 45 | if (!clen) 46 | return 0; 47 | udecoded = utf8decodebyte(c[0], &len); 48 | if (!BETWEEN(len, 1, UTF_SIZ)) 49 | return 1; 50 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { 51 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); 52 | if (type) 53 | return j; 54 | } 55 | if (j < len) 56 | return 0; 57 | *u = udecoded; 58 | utf8validate(u, len); 59 | 60 | return len; 61 | } 62 | 63 | Drw * 64 | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 65 | { 66 | Drw *drw = ecalloc(1, sizeof(Drw)); 67 | 68 | drw->dpy = dpy; 69 | drw->screen = screen; 70 | drw->root = root; 71 | drw->w = w; 72 | drw->h = h; 73 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 74 | drw->gc = XCreateGC(dpy, root, 0, NULL); 75 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 76 | 77 | return drw; 78 | } 79 | 80 | void 81 | drw_resize(Drw *drw, unsigned int w, unsigned int h) 82 | { 83 | if (!drw) 84 | return; 85 | 86 | drw->w = w; 87 | drw->h = h; 88 | if (drw->drawable) 89 | XFreePixmap(drw->dpy, drw->drawable); 90 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 91 | } 92 | 93 | void 94 | drw_free(Drw *drw) 95 | { 96 | XFreePixmap(drw->dpy, drw->drawable); 97 | XFreeGC(drw->dpy, drw->gc); 98 | free(drw); 99 | } 100 | 101 | /* This function is an implementation detail. Library users should use 102 | * drw_fontset_create instead. 103 | */ 104 | static Fnt * 105 | xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 106 | { 107 | Fnt *font; 108 | XftFont *xfont = NULL; 109 | FcPattern *pattern = NULL; 110 | 111 | if (fontname) { 112 | /* Using the pattern found at font->xfont->pattern does not yield the 113 | * same substitution results as using the pattern returned by 114 | * FcNameParse; using the latter results in the desired fallback 115 | * behaviour whereas the former just results in missing-character 116 | * rectangles being drawn, at least with some fonts. */ 117 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 118 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 119 | return NULL; 120 | } 121 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 122 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 123 | XftFontClose(drw->dpy, xfont); 124 | return NULL; 125 | } 126 | } else if (fontpattern) { 127 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 128 | fprintf(stderr, "error, cannot load font from pattern.\n"); 129 | return NULL; 130 | } 131 | } else { 132 | die("no font specified."); 133 | } 134 | 135 | /* Do not allow using color fonts. This is a workaround for a BadLength 136 | * error from Xft with color glyphs. Modelled on the Xterm workaround. See 137 | * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 138 | * https://lists.suckless.org/dev/1701/30932.html 139 | * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 140 | * and lots more all over the internet. 141 | */ 142 | FcBool iscol; 143 | if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { 144 | XftFontClose(drw->dpy, xfont); 145 | return NULL; 146 | } 147 | 148 | font = ecalloc(1, sizeof(Fnt)); 149 | font->xfont = xfont; 150 | font->pattern = pattern; 151 | font->h = xfont->ascent + xfont->descent; 152 | font->dpy = drw->dpy; 153 | 154 | return font; 155 | } 156 | 157 | static void 158 | xfont_free(Fnt *font) 159 | { 160 | if (!font) 161 | return; 162 | if (font->pattern) 163 | FcPatternDestroy(font->pattern); 164 | XftFontClose(font->dpy, font->xfont); 165 | free(font); 166 | } 167 | 168 | Fnt* 169 | drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 170 | { 171 | Fnt *cur, *ret = NULL; 172 | size_t i; 173 | 174 | if (!drw || !fonts) 175 | return NULL; 176 | 177 | for (i = 1; i <= fontcount; i++) { 178 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 179 | cur->next = ret; 180 | ret = cur; 181 | } 182 | } 183 | return (drw->fonts = ret); 184 | } 185 | 186 | void 187 | drw_fontset_free(Fnt *font) 188 | { 189 | if (font) { 190 | drw_fontset_free(font->next); 191 | xfont_free(font); 192 | } 193 | } 194 | 195 | void 196 | drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 197 | { 198 | if (!drw || !dest || !clrname) 199 | return; 200 | 201 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 202 | DefaultColormap(drw->dpy, drw->screen), 203 | clrname, dest)) 204 | die("error, cannot allocate color '%s'", clrname); 205 | } 206 | 207 | /* Wrapper to create color schemes. The caller has to call free(3) on the 208 | * returned color scheme when done using it. */ 209 | Clr * 210 | drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 211 | { 212 | size_t i; 213 | Clr *ret; 214 | 215 | /* need at least two colors for a scheme */ 216 | if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) 217 | return NULL; 218 | 219 | for (i = 0; i < clrcount; i++) 220 | drw_clr_create(drw, &ret[i], clrnames[i]); 221 | return ret; 222 | } 223 | 224 | void 225 | drw_setfontset(Drw *drw, Fnt *set) 226 | { 227 | if (drw) 228 | drw->fonts = set; 229 | } 230 | 231 | void 232 | drw_setscheme(Drw *drw, Clr *scm) 233 | { 234 | if (drw) 235 | drw->scheme = scm; 236 | } 237 | 238 | void 239 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 240 | { 241 | if (!drw || !drw->scheme) 242 | return; 243 | XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 244 | if (filled) 245 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 246 | else 247 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 248 | } 249 | 250 | int 251 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int filled, int invert) 252 | { 253 | char buf[1024]; 254 | int ty; 255 | unsigned int ew; 256 | XftDraw *d = NULL; 257 | Fnt *usedfont, *curfont, *nextfont; 258 | size_t i, len; 259 | int utf8strlen, utf8charlen, render = x || y || w || h; 260 | long utf8codepoint = 0; 261 | const char *utf8str; 262 | FcCharSet *fccharset; 263 | FcPattern *fcpattern; 264 | FcPattern *match; 265 | XftResult result; 266 | int charexists = 0; 267 | 268 | if (!drw || (render && !drw->scheme) || !text || !drw->fonts) 269 | return 0; 270 | 271 | if (!render) { 272 | w = ~w; 273 | } else { 274 | if (filled) { 275 | XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 276 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 277 | } 278 | d = XftDrawCreate(drw->dpy, drw->drawable, 279 | DefaultVisual(drw->dpy, drw->screen), 280 | DefaultColormap(drw->dpy, drw->screen)); 281 | x += lpad; 282 | w -= lpad; 283 | } 284 | 285 | usedfont = drw->fonts; 286 | while (1) { 287 | utf8strlen = 0; 288 | utf8str = text; 289 | nextfont = NULL; 290 | while (*text) { 291 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); 292 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { 293 | charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 294 | if (charexists) { 295 | if (curfont == usedfont) { 296 | utf8strlen += utf8charlen; 297 | text += utf8charlen; 298 | } else { 299 | nextfont = curfont; 300 | } 301 | break; 302 | } 303 | } 304 | 305 | if (!charexists || nextfont) 306 | break; 307 | else 308 | charexists = 0; 309 | } 310 | 311 | if (utf8strlen) { 312 | drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); 313 | /* shorten text if necessary */ 314 | for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) 315 | drw_font_getexts(usedfont, utf8str, len, &ew, NULL); 316 | 317 | if (len) { 318 | memcpy(buf, utf8str, len); 319 | buf[len] = '\0'; 320 | if (len < utf8strlen) 321 | for (i = len; i && i > len - 3; buf[--i] = '.') 322 | ; /* NOP */ 323 | 324 | if (render) { 325 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 326 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 327 | usedfont->xfont, x, ty, (XftChar8 *)buf, len); 328 | } 329 | x += ew; 330 | w -= ew; 331 | } 332 | } 333 | 334 | if (!*text) { 335 | break; 336 | } else if (nextfont) { 337 | charexists = 0; 338 | usedfont = nextfont; 339 | } else { 340 | /* Regardless of whether or not a fallback font is found, the 341 | * character must be drawn. */ 342 | charexists = 1; 343 | 344 | fccharset = FcCharSetCreate(); 345 | FcCharSetAddChar(fccharset, utf8codepoint); 346 | 347 | if (!drw->fonts->pattern) { 348 | /* Refer to the comment in xfont_create for more information. */ 349 | die("the first font in the cache must be loaded from a font string."); 350 | } 351 | 352 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); 353 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 354 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 355 | FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); 356 | 357 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 358 | FcDefaultSubstitute(fcpattern); 359 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 360 | 361 | FcCharSetDestroy(fccharset); 362 | FcPatternDestroy(fcpattern); 363 | 364 | if (match) { 365 | usedfont = xfont_create(drw, NULL, match); 366 | if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 367 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 368 | ; /* NOP */ 369 | curfont->next = usedfont; 370 | } else { 371 | xfont_free(usedfont); 372 | usedfont = drw->fonts; 373 | } 374 | } 375 | } 376 | } 377 | if (d) 378 | XftDrawDestroy(d); 379 | 380 | return x + (render ? w : 0); 381 | } 382 | 383 | void 384 | drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 385 | { 386 | if (!drw) 387 | return; 388 | 389 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 390 | XSync(drw->dpy, False); 391 | } 392 | 393 | unsigned int 394 | drw_fontset_getwidth(Drw *drw, const char *text) 395 | { 396 | if (!drw || !drw->fonts || !text) 397 | return 0; 398 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0, 0); 399 | } 400 | 401 | void 402 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 403 | { 404 | XGlyphInfo ext; 405 | 406 | if (!font || !text) 407 | return; 408 | 409 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 410 | if (w) 411 | *w = ext.xOff; 412 | if (h) 413 | *h = font->h; 414 | } 415 | 416 | Cur * 417 | drw_cur_create(Drw *drw, int shape) 418 | { 419 | Cur *cur; 420 | 421 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 422 | return NULL; 423 | 424 | cur->cursor = XCreateFontCursor(drw->dpy, shape); 425 | 426 | return cur; 427 | } 428 | 429 | void 430 | drw_cur_free(Drw *drw, Cur *cursor) 431 | { 432 | if (!cursor) 433 | return; 434 | 435 | XFreeCursor(drw->dpy, cursor->cursor); 436 | free(cursor); 437 | } 438 | -------------------------------------------------------------------------------- /drw.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | typedef struct { 4 | Cursor cursor; 5 | } Cur; 6 | 7 | typedef struct Fnt { 8 | Display *dpy; 9 | unsigned int h; 10 | XftFont *xfont; 11 | FcPattern *pattern; 12 | struct Fnt *next; 13 | } Fnt; 14 | 15 | enum { ColFg, ColBg }; /* Clr scheme index */ 16 | typedef XftColor Clr; 17 | 18 | typedef struct { 19 | unsigned int w, h; 20 | Display *dpy; 21 | int screen; 22 | Window root; 23 | Drawable drawable; 24 | GC gc; 25 | Clr *scheme; 26 | Fnt *fonts; 27 | } Drw; 28 | 29 | /* Drawable abstraction */ 30 | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); 31 | void drw_resize(Drw *drw, unsigned int w, unsigned int h); 32 | void drw_free(Drw *drw); 33 | 34 | /* Fnt abstraction */ 35 | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); 36 | void drw_fontset_free(Fnt* set); 37 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); 38 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); 39 | 40 | /* Colorscheme abstraction */ 41 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); 42 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); 43 | 44 | /* Cursor abstraction */ 45 | Cur *drw_cur_create(Drw *drw, int shape); 46 | void drw_cur_free(Drw *drw, Cur *cursor); 47 | 48 | /* Drawing context manipulation */ 49 | void drw_setfontset(Drw *drw, Fnt *set); 50 | void drw_setscheme(Drw *drw, Clr *scm); 51 | 52 | /* Drawing functions */ 53 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); 54 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int filled, int invert); 55 | 56 | /* Map functions */ 57 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); 58 | -------------------------------------------------------------------------------- /examples/brtwjt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # adjust brightness using wjt and http://haikarainen.github.io/light/ 4 | 5 | initlvl="$(light)" 6 | 7 | wjt -p "brightness:" -x "$initlvl" "$@" | while read lvl; do 8 | light -S "$lvl" 9 | done 10 | -------------------------------------------------------------------------------- /examples/volwjt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # adjust volume using wjt and amixer 4 | 5 | initvol="$(amixer get Master | tr -d '[]%' | awk '/Left: Playback/ { print $5 }')" 6 | 7 | wjt -p "volume:" -z m -x "$initvol" "$@" | while read vol; do 8 | case "$vol" in 9 | m) 10 | amixer -q set Master toggle 11 | ;; 12 | *) 13 | amixer -q set Master "$vol%" 14 | ;; 15 | esac 16 | done 17 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | void * 10 | ecalloc(size_t nmemb, size_t size) 11 | { 12 | void *p; 13 | 14 | if (!(p = calloc(nmemb, size))) 15 | die("calloc:"); 16 | return p; 17 | } 18 | 19 | void 20 | die(const char *fmt, ...) { 21 | va_list ap; 22 | 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | 27 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 28 | fputc(' ', stderr); 29 | perror(NULL); 30 | } else { 31 | fputc('\n', stderr); 32 | } 33 | 34 | exit(1); 35 | } 36 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 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 *fmt, ...); 8 | void *ecalloc(size_t nmemb, size_t size); 9 | -------------------------------------------------------------------------------- /wjt.1: -------------------------------------------------------------------------------- 1 | .TH WJT 1 wjt\-VERSION 2 | .SH NAME 3 | wjt \- slider widget 4 | .SH SYNOPSIS 5 | .B wjt 6 | .RB [ \-v ] 7 | .RB [ \-b ] 8 | .RB [ \-lv ] 9 | .RB [ \-le ] 10 | .RB [ \-m 11 | .IR monnum ] 12 | .RB [ \-w 13 | .IR winid ] 14 | .RB [ \-p 15 | .IR prompt ] 16 | .RB [ \-f 17 | .IR font ] 18 | .RB [ \-pb 19 | .IR color ] 20 | .RB [ \-pf 21 | .IR color ] 22 | .RB [ \-sb 23 | .IR color ] 24 | .RB [ \-sf 25 | .IR color ] 26 | .RB [ \-vb 27 | .IR color ] 28 | .RB [ \-vf 29 | .IR color ] 30 | .RB [ \-l 31 | .IR lower ] 32 | .RB [ \-u 33 | .IR upper ] 34 | .RB [ \-s 35 | .IR step ] 36 | .RB [ \-j 37 | .IR jump ] 38 | .RB [ \-x 39 | .IR value ] 40 | .RB [ \-z 41 | .IR special ] 42 | .P 43 | .SH DESCRIPTION 44 | .B wjt 45 | is a slider widget for the X Window System that allows the user to select 46 | values within a range using the keyboard or mouse. It presents a bar at the 47 | top or bottom of the display with a slider area including legend labels, and 48 | optionally, a prompt on the left. When the user adjusts the slider position, 49 | its value is printed to stdout. wjt grabs keyboard and mouse input while 50 | running. 51 | .P 52 | wjt was inspired by, and its code is based on, 53 | .BR dmenu (1). 54 | .P 55 | .SH OPTIONS 56 | .TP 57 | .B \-v 58 | Print version information to stdout and exit. 59 | .TP 60 | .B \-b 61 | Display wjt at the bottom of the display. 62 | .TP 63 | .B \-le 64 | Disable drawing labels for the slider extents. 65 | .TP 66 | .B \-lv 67 | Disable drawing a label for the slider value. 68 | .TP 69 | .BI \-m " monnum" 70 | Display wjt on monitor number monnum. Monitor numbers start at 0. 71 | .TP 72 | .BI \-w " winid" 73 | Embed wjt into the window with id winid. 74 | .TP 75 | .BI \-p " prompt" 76 | Specify a prompt to be displayed to the left of the slider. 77 | .TP 78 | .BI \-f " font" 79 | Specify the font or font set to use. 80 | .TP 81 | .BI \-pb " color" 82 | Specify the prompt background color. 83 | .IR #RGB , 84 | .IR #RRGGBB , 85 | and X color names are supported. 86 | .TP 87 | .BI \-pf " color" 88 | Specify the prompt foreground color. 89 | .TP 90 | .BI \-sb " color" 91 | Specify the color of the slider beyond the slider value. 92 | .TP 93 | .BI \-sf " color" 94 | Specify the extent label color. 95 | .TP 96 | .BI \-vb " color" 97 | Specify the color of the slider up to the slider value. 98 | .TP 99 | .BI \-vf " color" 100 | Specify the slider value label color. 101 | .TP 102 | .BI \-l " lower" 103 | Specify the lower bound of the slider (default: 0). 104 | .TP 105 | .BI \-u " upper" 106 | Specify the upper bound of the slider (default: 100). 107 | .TP 108 | .BI \-s " step" 109 | Specify the size of a small change in the slider value (default: 1). 110 | .TP 111 | .BI \-j " jump" 112 | Specify the size of a large change in the slider value (default: 10). 113 | .TP 114 | .BI \-x " value" 115 | Specify the initial value of the slider (default: 0). 116 | .TP 117 | .BI \-z " special" 118 | Specify special text to emit when the user presses Space or clicks the middle 119 | mouse button. 120 | .SH MOUSE CONTROLS 121 | .TP 122 | .B Left click or drag 123 | Adjust the slider value. 124 | .TP 125 | .B Middle click 126 | Emit special text if specified. 127 | .TP 128 | .B ScrollUp 129 | Increase the slider value one step. 130 | .TP 131 | .B ScrollDown 132 | Decrease the slider value one step. 133 | .TP 134 | .B C-ScrollUp 135 | Increase the slider value one jump. 136 | .TP 137 | .B C-ScrollDown 138 | Decrease the slider value one jump. 139 | .TP 140 | .B Right click 141 | Exit with status 0. 142 | .SH KEYBOARD CONTROLS 143 | .TP 144 | .B ` 1 2 3 4 5 6 7 8 9 0 145 | Map to 10% increments through the slider range: ` to 0%, 1 to 10%, ..., 9 to 90%, 0 to 100%. 146 | .TP 147 | .B Home C\-a g 148 | Set the slider value to the lower bound. 149 | .TP 150 | .B End C\-e G 151 | Set the slider value to the upper bound. 152 | .TP 153 | .B Left C\-b h - 154 | Decrease the slider value one step. 155 | .TP 156 | .B Right C\-f l + = 157 | Increase the slider value one step. 158 | .TP 159 | .B PageDown Down C\-p j 160 | Decrease the slider value one jump. 161 | .TP 162 | .B PageUp Up C\-n k 163 | Increase the slider value one jump. 164 | .TP 165 | .B Space 166 | Emit special text if specified. 167 | .TP 168 | .B Enter C\-m C\-j 169 | Exit with status 0. 170 | .TP 171 | .B Escape C\-[ C\-c 172 | Exit with status 1. 173 | .SH SEE ALSO 174 | .BR dmenu (1) 175 | -------------------------------------------------------------------------------- /wjt.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #ifdef XINERAMA 16 | #include 17 | #endif 18 | #include 19 | 20 | #include "drw.h" 21 | #include "util.h" 22 | 23 | /* macros */ 24 | #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ 25 | * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) 26 | #define LENGTH(X) (sizeof X / sizeof X[0]) 27 | #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 28 | 29 | #define MAXVAL 1000000000 30 | #define VBUFSIZE 12 /* enough to hold negation of above */ 31 | #define VERROR " must be a resonably sized integer" 32 | 33 | /* enums */ 34 | enum { SchemePrompt, SchemeSlider, SchemeValue, SchemeLast }; /* color schemes */ 35 | 36 | static char *embed; 37 | static int sw, sh; 38 | static int winx; 39 | static int sx = 0; 40 | static int promptw = 0; 41 | static int lrpad; /* sum of left and right padding */ 42 | static int mon = -1, screen; 43 | 44 | static char minstr[VBUFSIZE]; 45 | static char maxstr[VBUFSIZE]; 46 | static char valstr[VBUFSIZE]; 47 | static int minw; 48 | static int maxw; 49 | static int valw; 50 | static int valx; 51 | static int valout; 52 | static int val = MAXVAL + 1; 53 | 54 | static Display *dpy; 55 | static Window root, parentwin, win; 56 | static XIC xic; 57 | 58 | static Drw *drw; 59 | static Clr *scheme[SchemeLast]; 60 | 61 | #include "config.h" 62 | 63 | static void 64 | quit(int status) 65 | { 66 | size_t i; 67 | 68 | XUngrabKeyboard(dpy, CurrentTime); 69 | XUngrabPointer(dpy, CurrentTime); 70 | for (i = 0; i < SchemeLast; i++) 71 | free(scheme[i]); 72 | drw_free(drw); 73 | XSync(dpy, False); 74 | XCloseDisplay(dpy); 75 | exit(status); 76 | } 77 | 78 | static float 79 | lerp(float a0, float a1, float b0, float b1, float x) 80 | { 81 | return a0 + (a1 - a0) * (x - b0) / (b1 - b0); 82 | } 83 | 84 | static void 85 | drawslider(void) 86 | { 87 | drw_setscheme(drw, scheme[SchemeSlider]); 88 | drw_rect(drw, 0, 0, sw, sh, 1, 1); 89 | if (prompt && *prompt) { 90 | drw_setscheme(drw, scheme[SchemePrompt]); 91 | drw_text(drw, 0, 0, promptw, sh, lrpad / 2, prompt, 1, 0); 92 | } 93 | drw_setscheme(drw, scheme[SchemeValue]); 94 | drw_rect(drw, sx, 0, valx, sh, 1, 1); 95 | if (labelval) 96 | drw_text(drw, sx + (sw - sx) / 2 - valw / 2, 0, valw, sh, lrpad / 2, valstr, 0, 0); 97 | if (labelexts) { 98 | drw_setscheme(drw, scheme[SchemeSlider]); 99 | drw_text(drw, sx, 0, minw, sh, lrpad / 2, minstr, 0, 0); 100 | drw_text(drw, sw - maxw, 0, maxw, sh, lrpad / 2, maxstr, 0, 0); 101 | } 102 | drw_map(drw, win, 0, 0, sw, sh); 103 | } 104 | 105 | static void 106 | grabfocus(void) 107 | { 108 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; 109 | Window focuswin; 110 | int i, revertwin; 111 | 112 | for (i = 0; i < 100; ++i) { 113 | XGetInputFocus(dpy, &focuswin, &revertwin); 114 | if (focuswin == win) 115 | return; 116 | XSetInputFocus(dpy, win, RevertToParent, CurrentTime); 117 | nanosleep(&ts, NULL); 118 | } 119 | die("cannot grab focus"); 120 | } 121 | 122 | static void 123 | grabinput(void) 124 | { 125 | struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; 126 | int ownkbd = 0; 127 | int ownptr = 0; 128 | int i; 129 | 130 | if (embed) 131 | return; 132 | /* try to grab keyboard and pointer, we may have to wait for another process to ungrab */ 133 | for (i = 0; i < 1000; i++) { 134 | if (!ownkbd) 135 | ownkbd = (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, 136 | GrabModeAsync, CurrentTime) == GrabSuccess); 137 | if (!ownptr) 138 | ownptr = (XGrabPointer(dpy, DefaultRootWindow(dpy), True, 139 | ButtonPressMask | ButtonReleaseMask | Button1MotionMask, 140 | GrabModeAsync, GrabModeAsync, None, None, 141 | CurrentTime) == GrabSuccess); 142 | if (ownkbd && ownptr) 143 | return; 144 | nanosleep(&ts, NULL); 145 | } 146 | if (!ownkbd) 147 | die("cannot grab keyboard"); 148 | die("cannot grab pointer"); 149 | } 150 | 151 | static void 152 | adjustval(int v) 153 | { 154 | if (v < min) 155 | v = min; 156 | else if (v > max) 157 | v = max; 158 | valx = (int)lerp(0, sw - 1 - sx, min, max, v); 159 | if (v != val) { 160 | val = v; 161 | snprintf(valstr, VBUFSIZE, "%d", val); 162 | valw = TEXTW(valstr); 163 | } 164 | } 165 | 166 | static void 167 | updateval(int v) 168 | { 169 | adjustval(v); 170 | if (val != valout) { 171 | valout = val; 172 | puts(valstr); 173 | fflush(stdout); 174 | } 175 | } 176 | 177 | static void 178 | xtoval(int x) 179 | { 180 | float fv; 181 | int v; 182 | 183 | fv = lerp(min, max, sx, sw - 1, x); 184 | v = (int)fv; 185 | if (v > min && v < max) { 186 | v = (int)(fv / step + copysignf(0.5, fv)) * step; 187 | } 188 | updateval(v); 189 | } 190 | 191 | static void 192 | printspecial(void) 193 | { 194 | if (special && *special) { 195 | puts(special); 196 | fflush(stdout); 197 | } 198 | } 199 | 200 | static void 201 | keypress(XKeyEvent *ev) 202 | { 203 | char buf[32]; 204 | KeySym ksym = NoSymbol; 205 | Status status; 206 | 207 | XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); 208 | if (status == XBufferOverflow) 209 | return; 210 | if (ev->state & ControlMask) { 211 | switch(ksym) { 212 | case XK_a: 213 | ksym = XK_Home; 214 | break; 215 | case XK_b: 216 | ksym = XK_Left; 217 | break; 218 | case XK_c: 219 | case XK_bracketleft: 220 | ksym = XK_Escape; 221 | break; 222 | case XK_e: 223 | ksym = XK_End; 224 | break; 225 | case XK_f: 226 | ksym = XK_Right; 227 | break; 228 | case XK_p: 229 | ksym = XK_Down; 230 | break; 231 | case XK_n: 232 | ksym = XK_Up; 233 | break; 234 | case XK_j: 235 | case XK_m: 236 | ksym = XK_Return; 237 | break; 238 | default: 239 | return; 240 | } 241 | } 242 | 243 | switch(ksym) { 244 | case XK_space: 245 | printspecial(); 246 | break; 247 | case XK_KP_Enter: 248 | case XK_Return: 249 | quit(0); 250 | case XK_Escape: 251 | quit(1); 252 | case XK_h: 253 | case XK_minus: 254 | case XK_Left: 255 | updateval(val - step); 256 | break; 257 | case XK_l: 258 | case XK_plus: 259 | case XK_equal: 260 | case XK_Right: 261 | updateval(val + step); 262 | break; 263 | case XK_j: 264 | case XK_Down: 265 | case XK_Page_Down: 266 | updateval(val - jump); 267 | break; 268 | case XK_k: 269 | case XK_Up: 270 | case XK_Page_Up: 271 | updateval(val + jump); 272 | break; 273 | case XK_g: 274 | case XK_Home: 275 | updateval(min); 276 | break; 277 | case XK_G: 278 | case XK_End: 279 | updateval(max); 280 | break; 281 | /* map number row to deciles */ 282 | case XK_grave: updateval(min); break; 283 | case XK_1: updateval(min + 1 * (max - min) / 10); break; 284 | case XK_2: updateval(min + 2 * (max - min) / 10); break; 285 | case XK_3: updateval(min + 3 * (max - min) / 10); break; 286 | case XK_4: updateval(min + 4 * (max - min) / 10); break; 287 | case XK_5: updateval(min + 5 * (max - min) / 10); break; 288 | case XK_6: updateval(min + 6 * (max - min) / 10); break; 289 | case XK_7: updateval(min + 7 * (max - min) / 10); break; 290 | case XK_8: updateval(min + 8 * (max - min) / 10); break; 291 | case XK_9: updateval(min + 9 * (max - min) / 10); break; 292 | case XK_0: updateval(max); break; 293 | default: 294 | return; 295 | } 296 | drawslider(); 297 | } 298 | 299 | static void 300 | buttonpress(XButtonPressedEvent *ev) 301 | { 302 | switch (ev->button) { 303 | case Button1: 304 | xtoval(ev->x_root - winx); 305 | break; 306 | case Button4: 307 | updateval(val + (ev->state & ControlMask ? jump : step)); 308 | break; 309 | case Button5: 310 | updateval(val - (ev->state & ControlMask ? jump : step)); 311 | break; 312 | default: 313 | return; 314 | } 315 | drawslider(); 316 | } 317 | 318 | static void 319 | buttonrelease(XButtonReleasedEvent *ev) 320 | { 321 | switch (ev->button) { 322 | case Button1: 323 | xtoval(ev->x_root - winx); 324 | break; 325 | case Button2: 326 | printspecial(); 327 | return; 328 | case Button3: 329 | quit(0); 330 | default: 331 | return; 332 | } 333 | drawslider(); 334 | } 335 | 336 | static void 337 | buttonmove(XMotionEvent *ev) 338 | { 339 | xtoval(ev->x_root - winx); 340 | drawslider(); 341 | } 342 | 343 | static void 344 | run(void) 345 | { 346 | XEvent ev; 347 | 348 | while (!XNextEvent(dpy, &ev)) { 349 | if (XFilterEvent(&ev, win)) 350 | continue; 351 | switch(ev.type) { 352 | case DestroyNotify: 353 | if (ev.xdestroywindow.window != win) 354 | break; 355 | quit(1); 356 | case Expose: 357 | if (ev.xexpose.count == 0) 358 | drw_map(drw, win, 0, 0, sw, sh); 359 | break; 360 | case FocusIn: 361 | /* regrab focus from parent window */ 362 | if (ev.xfocus.window != win) 363 | grabfocus(); 364 | break; 365 | case KeyPress: 366 | keypress(&ev.xkey); 367 | break; 368 | case ButtonPress: 369 | buttonpress(&ev.xbutton); 370 | break; 371 | case ButtonRelease: 372 | buttonrelease(&ev.xbutton); 373 | break; 374 | case MotionNotify: 375 | buttonmove(&ev.xmotion); 376 | break; 377 | case VisibilityNotify: 378 | if (ev.xvisibility.state != VisibilityUnobscured) 379 | XRaiseWindow(dpy, win); 380 | break; 381 | } 382 | } 383 | } 384 | 385 | static void 386 | setup(void) 387 | { 388 | int x, y, i = 0; 389 | unsigned int du; 390 | XSetWindowAttributes swa; 391 | XIM xim; 392 | Window w, dw, *dws; 393 | XWindowAttributes wa; 394 | XClassHint ch = {"wjt", "wjt"}; 395 | #ifdef XINERAMA 396 | XineramaScreenInfo *info; 397 | Window pw; 398 | int a, j, di, n, area = 0; 399 | #endif 400 | 401 | /* init appearance */ 402 | scheme[SchemeSlider] = drw_scm_create(drw, colors[SchemeSlider], 2); 403 | scheme[SchemePrompt] = drw_scm_create(drw, colors[SchemePrompt], 2); 404 | scheme[SchemeValue] = drw_scm_create(drw, colors[SchemeValue], 2); 405 | 406 | /* calculate slider geometry */ 407 | sh = drw->fonts->h + 2; 408 | #ifdef XINERAMA 409 | if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { 410 | XGetInputFocus(dpy, &w, &di); 411 | if (mon >= 0 && mon < n) 412 | i = mon; 413 | else if (w != root && w != PointerRoot && w != None) { 414 | /* find top-level window containing current input focus */ 415 | do { 416 | if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) 417 | XFree(dws); 418 | } while (w != root && w != pw); 419 | /* find xinerama screen with which the window intersects most */ 420 | if (XGetWindowAttributes(dpy, pw, &wa)) 421 | for (j = 0; j < n; j++) 422 | if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { 423 | area = a; 424 | i = j; 425 | } 426 | } 427 | /* no focused window is on screen, so use pointer location instead */ 428 | if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) 429 | for (i = 0; i < n; i++) 430 | if (INTERSECT(x, y, 1, 1, info[i])) 431 | break; 432 | 433 | x = info[i].x_org; 434 | y = info[i].y_org + (topbar ? 0 : info[i].height - sh); 435 | sw = info[i].width; 436 | XFree(info); 437 | } else 438 | #endif 439 | { 440 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 441 | die("could not get embedding window attributes: 0x%lx", 442 | parentwin); 443 | x = 0; 444 | y = topbar ? 0 : wa.height - sh; 445 | sw = wa.width; 446 | } 447 | winx = x; 448 | 449 | if (prompt && *prompt) { 450 | promptw = TEXTW(prompt); 451 | sx = promptw + lrpad / 4; 452 | } 453 | 454 | /* create slider window */ 455 | swa.override_redirect = True; 456 | swa.background_pixel = scheme[SchemeSlider][ColBg].pixel; 457 | swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | 458 | Button1MotionMask | VisibilityChangeMask; 459 | win = XCreateWindow(dpy, parentwin, x, y, sw, sh, 0, 460 | CopyFromParent, CopyFromParent, CopyFromParent, 461 | CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); 462 | XSetClassHint(dpy, win, &ch); 463 | 464 | /* input methods */ 465 | if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) 466 | die("XOpenIM failed: could not open input device"); 467 | 468 | xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 469 | XNClientWindow, win, XNFocusWindow, win, NULL); 470 | 471 | XMapRaised(dpy, win); 472 | if (embed) { 473 | XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); 474 | if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { 475 | for (i = 0; i < du && dws[i] != win; ++i) 476 | XSelectInput(dpy, dws[i], FocusChangeMask); 477 | XFree(dws); 478 | } 479 | grabfocus(); 480 | } 481 | drw_resize(drw, sw, sh); 482 | 483 | valout = initval; 484 | adjustval(initval); 485 | snprintf(minstr, VBUFSIZE, "%d", min); 486 | snprintf(maxstr, VBUFSIZE, "%d", max); 487 | minw = TEXTW(minstr); 488 | maxw = TEXTW(maxstr); 489 | 490 | drawslider(); 491 | } 492 | 493 | static void 494 | usage(void) 495 | { 496 | fputs("usage: wjt [-v] [-b] [-lv] [-le] [-m monnum] [-w winid] [-p prompt]\n" 497 | " [-f font] [-pb color] [-pf color] [-sb color] [-sf color]\n" 498 | " [-vb color] [-vf color] [-l lower] [-u upper] [-s step]\n" 499 | " [-j jump] [-x value] [-z special]\n", stderr); 500 | exit(1); 501 | } 502 | 503 | static int 504 | valarg(char *arg, int *ok) 505 | { 506 | long x; 507 | char *p; 508 | 509 | x = strtol(arg, &p, 0); 510 | if (ok) { 511 | *ok = (p != arg && labs(x) <= MAXVAL); 512 | } 513 | return x; 514 | } 515 | 516 | int 517 | main(int argc, char *argv[]) 518 | { 519 | XWindowAttributes wa; 520 | int i; 521 | int ok; 522 | 523 | for (i = 1; i < argc; i++) { 524 | /* these options take no arguments */ 525 | if (!strcmp(argv[i], "-v")) { 526 | puts("wjt-"VERSION); 527 | exit(0); 528 | } else if (!strcmp(argv[i], "-b")) /* invert bar vertical screen location */ 529 | topbar = !topbar; 530 | else if (!strcmp(argv[i], "-lv")) /* invert whether to display value label */ 531 | labelval = !labelval; 532 | else if (!strcmp(argv[i], "-le")) /* invert whether to display extent labels */ 533 | labelexts = !labelexts; 534 | else if (i + 1 == argc) 535 | usage(); 536 | /* these options take one argument */ 537 | else if (!strcmp(argv[i], "-m")) /* monitor number */ 538 | mon = atoi(argv[++i]); 539 | else if (!strcmp(argv[i], "-w")) /* embedding window id */ 540 | embed = argv[++i]; 541 | else if (!strcmp(argv[i], "-p")) /* adds prompt to left of slider */ 542 | prompt = argv[++i]; 543 | else if (!strcmp(argv[i], "-f")) /* font or font set */ 544 | fonts[0] = argv[++i]; 545 | else if (!strcmp(argv[i], "-pb")) /* prompt background color */ 546 | colors[SchemePrompt][ColBg] = argv[++i]; 547 | else if (!strcmp(argv[i], "-pf")) /* prompt foreground color */ 548 | colors[SchemePrompt][ColFg] = argv[++i]; 549 | else if (!strcmp(argv[i], "-sb")) /* slider background color */ 550 | colors[SchemeSlider][ColBg] = argv[++i]; 551 | else if (!strcmp(argv[i], "-sf")) /* slider foreground color */ 552 | colors[SchemeSlider][ColFg] = argv[++i]; 553 | else if (!strcmp(argv[i], "-vb")) /* value background color */ 554 | colors[SchemeValue][ColBg] = argv[++i]; 555 | else if (!strcmp(argv[i], "-vf")) /* value foreground color */ 556 | colors[SchemeValue][ColFg] = argv[++i]; 557 | else if (!strcmp(argv[i], "-l")) { /* lower bound */ 558 | min = valarg(argv[++i], &ok); 559 | if (!ok) 560 | die("lower bound"VERROR); 561 | } else if (!strcmp(argv[i], "-u")) { /* upper bound */ 562 | max = valarg(argv[++i], &ok); 563 | if (!ok) 564 | die("upper bound"VERROR); 565 | } else if (!strcmp(argv[i], "-j")) { /* jump */ 566 | jump = valarg(argv[++i], &ok); 567 | if (!ok) 568 | die("jump"VERROR); 569 | } else if (!strcmp(argv[i], "-s")) { /* step */ 570 | step = valarg(argv[++i], &ok); 571 | if (!ok) 572 | die("step"VERROR); 573 | } else if (!strcmp(argv[i], "-x")) { /* initial value */ 574 | initval = valarg(argv[++i], &ok); 575 | if (!ok) 576 | die("initial value"VERROR); 577 | } else if (!strcmp(argv[i], "-z")) /* special text */ 578 | special = argv[++i]; 579 | else 580 | usage(); 581 | } 582 | 583 | if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 584 | fputs("warning: no locale support\n", stderr); 585 | if (!(dpy = XOpenDisplay(NULL))) 586 | die("cannot open display"); 587 | screen = DefaultScreen(dpy); 588 | root = RootWindow(dpy, screen); 589 | if (!embed || !(parentwin = strtol(embed, NULL, 0))) 590 | parentwin = root; 591 | if (!XGetWindowAttributes(dpy, parentwin, &wa)) 592 | die("could not get embedding window attributes: 0x%lx", 593 | parentwin); 594 | drw = drw_create(dpy, screen, root, wa.width, wa.height); 595 | if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 596 | die("no fonts could be loaded."); 597 | lrpad = drw->fonts->h; 598 | 599 | if (max <= min) 600 | die("upper bound must be greater than lower bound"); 601 | if (step < 1) 602 | die("step must be positive"); 603 | if (jump <= step) 604 | die("jump must not be less than step"); 605 | 606 | grabinput(); 607 | setup(); 608 | run(); 609 | 610 | return 1; /* unreachable */ 611 | } 612 | --------------------------------------------------------------------------------