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 | } | ||