| Hash | Commit message | Author | Date | Files | + | - |
1 | commit 739558a6b68001e761f277d09654471be4799d20 (HEAD -> master) |
2 | Author: Connor Etherington <[email protected]> |
3 | Date: Fri Sep 13 23:32:34 2024 +0200 |
4 | |
5 | Update 13-September-2024___23:32:34 |
6 | --- |
7 | LICENSE | 31 + |
8 | Makefile | 64 ++ |
9 | PKGBUILD | 47 ++ |
10 | arg.h | 46 ++ |
11 | autolinux-dmenu-archive-0.2.2.tar.gz.sig | Bin 0 -> 566 bytes |
12 | config.h | 45 + |
13 | config.mk | 31 + |
14 | dmenu.1 | 225 +++++ |
15 | dmenu.c | 1097 +++++++++++++++++++++++++ |
16 | dmenu_path | 13 + |
17 | dmenu_run | 2 + |
18 | drw.c | 436 ++++++++++ |
19 | drw.h | 57 ++ |
20 | patches/dmenu-border-4.9.diff | 25 + |
21 | patches/dmenu-center-20200111-8cd37e1.diff | 120 +++ |
22 | patches/dmenu-fuzzyhighlight-4.9.diff | 152 ++++ |
23 | patches/dmenu-fuzzymatch-4.9.diff | 163 ++++ |
24 | patches/dmenu-grid-4.9.diff | 107 +++ |
25 | patches/dmenu-lineheight-4.9.diff | 94 +++ |
26 | patches/dmenu-morecolor-20190922-4bf895b.diff | 47 ++ |
27 | patches/dmenu-mousesupport-5.0.diff | 144 ++++ |
28 | patches/dmenu-numbers-4.9.diff | 81 ++ |
29 | stest.1 | 90 ++ |
30 | stest.c | 109 +++ |
31 | util.c | 35 + |
32 | util.h | 8 + |
33 | 26 files changed, 3269 insertions(+) |
34 | |
35 | diff --git a/LICENSE b/LICENSE |
36 | new file mode 100644 |
37 | index 0000000..0d19151 |
38 | --- /dev/null |
39 | +++ b/LICENSE |
40 | @@ -0,0 +1,31 @@ |
41 | +MIT/X Consortium License |
42 | + |
43 | +© 2006-2019 Anselm R Garbe <[email protected]> |
44 | +© 2006-2008 Sander van Dijk <[email protected]> |
45 | +© 2006-2007 Michał Janeczek <[email protected]> |
46 | +© 2007 Kris Maglione <[email protected]> |
47 | +© 2009 Gottox <[email protected]> |
48 | +© 2009 Markus Schnalke <[email protected]> |
49 | +© 2009 Evan Gates <[email protected]> |
50 | +© 2010-2012 Connor Lane Smith <[email protected]> |
51 | +© 2014-2020 Hiltjo Posthuma <[email protected]> |
52 | +© 2015-2019 Quentin Rameau <[email protected]> |
53 | +© 2019-2024 Connor Etherington <[email protected]> |
54 | + |
55 | +Permission is hereby granted, free of charge, to any person obtaining a |
56 | +copy of this software and associated documentation files (the "Software"), |
57 | +to deal in the Software without restriction, including without limitation |
58 | +the rights to use, copy, modify, merge, publish, distribute, sublicense, |
59 | +and/or sell copies of the Software, and to permit persons to whom the |
60 | +Software is furnished to do so, subject to the following conditions: |
61 | + |
62 | +The above copyright notice and this permission notice shall be included in |
63 | +all copies or substantial portions of the Software. |
64 | + |
65 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
66 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
67 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
68 | +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
69 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
70 | +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
71 | +DEALINGS IN THE SOFTWARE. |
72 | diff --git a/Makefile b/Makefile |
73 | new file mode 100644 |
74 | index 0000000..a03a95c |
75 | --- /dev/null |
76 | +++ b/Makefile |
77 | @@ -0,0 +1,64 @@ |
78 | +# dmenu - dynamic menu |
79 | +# See LICENSE file for copyright and license details. |
80 | + |
81 | +include config.mk |
82 | + |
83 | +SRC = drw.c dmenu.c stest.c util.c |
84 | +OBJ = $(SRC:.c=.o) |
85 | + |
86 | +all: options dmenu stest |
87 | + |
88 | +options: |
89 | + @echo dmenu build options: |
90 | + @echo "CFLAGS = $(CFLAGS)" |
91 | + @echo "LDFLAGS = $(LDFLAGS)" |
92 | + @echo "CC = $(CC)" |
93 | + |
94 | +.c.o: |
95 | + $(CC) -c $(CFLAGS) $< |
96 | + |
97 | +config.h: |
98 | + cp config.def.h $@ |
99 | + |
100 | +$(OBJ): arg.h config.h config.mk drw.h |
101 | + |
102 | +dmenu: dmenu.o drw.o util.o |
103 | + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) |
104 | + |
105 | +stest: stest.o |
106 | + $(CC) -o $@ stest.o $(LDFLAGS) |
107 | + |
108 | +clean: |
109 | + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz |
110 | + |
111 | +dist: clean |
112 | + mkdir -p dmenu-$(VERSION) |
113 | + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ |
114 | + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ |
115 | + dmenu-$(VERSION) |
116 | + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) |
117 | + gzip dmenu-$(VERSION).tar |
118 | + rm -rf dmenu-$(VERSION) |
119 | + |
120 | +install: all |
121 | + mkdir -p $(DESTDIR)$(PREFIX)/bin |
122 | + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin |
123 | + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu |
124 | + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path |
125 | + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run |
126 | + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest |
127 | + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 |
128 | + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 |
129 | + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 |
130 | + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 |
131 | + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 |
132 | + |
133 | +uninstall: |
134 | + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ |
135 | + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ |
136 | + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ |
137 | + $(DESTDIR)$(PREFIX)/bin/stest\ |
138 | + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ |
139 | + $(DESTDIR)$(MANPREFIX)/man1/stest.1 |
140 | + |
141 | +.PHONY: all options clean dist install uninstall |
142 | diff --git a/PKGBUILD b/PKGBUILD |
143 | new file mode 100644 |
144 | index 0000000..d4c071f |
145 | --- /dev/null |
146 | +++ b/PKGBUILD |
147 | @@ -0,0 +1,47 @@ |
148 | +# Maintainer: Connor Etherington <[email protected]> |
149 | +# --- |
150 | +pkgname="autolinux-dmenu" |
151 | +pkgdesc="The AutoLinux fork of dmenu" |
152 | +pkgver=0.2.2 |
153 | +pkgrel=1 |
154 | +arch=(x86_64) |
155 | +url="https://gitlab.com/a4to/${pkgname}" |
156 | +license=('MIT') |
157 | +depends=(ttf-hack) |
158 | +provides=(dmenu) |
159 | +conflicts=(dmenu) |
160 | +replaces=(dmenu) |
161 | +source=( |
162 | + "https://gitlab.com/a4to/autolinux-dmenu/-/archive/archive-0.2.2/autolinux-dmenu-archive-0.2.2.tar.gz" |
163 | +) |
164 | +sha512sums=( |
165 | + '1cd4453a8e54e3da6eed704c5e6191c9a8c4eb6a530c78983d1dba813ba2db33c1c09dcbb67be124573456e62ead63e6843a7376632cd432bb8a98ddcddcf64d' |
166 | +) |
167 | +md5sums=( |
168 | + '0e1422f2076d2dedd9a911161b8c3ccc' |
169 | +) |
170 | +validpgpkeys=( |
171 | + '81BACEEBC3EA26E127166E4A819BB92A9A48160E' |
172 | +) |
173 | + |
174 | +#pkgver() { |
175 | +# cd "$srcdir/${pkgname}-${pkgver}-${pkgrel}-${arch}" >/dev/null 2>&1 || |
176 | +# cd "$srcdir/${pkgname}" |
177 | +# printf "0.2.r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" |
178 | +#} |
179 | + |
180 | +build() { |
181 | + cd "$srcdir/${pkgname}-${pkgver}-${pkgrel}-${arch}" >/dev/null 2>&1 || |
182 | + cd "$srcdir/${pkgname}" |
183 | + make X11INC=/usr/include/X11 X11LIB=/usr/lib/X11 |
184 | +} |
185 | + |
186 | +package() { |
187 | + cd "$srcdir/${pkgname}-${pkgver}-${pkgrel}-${arch}" >/dev/null 2>&1 || |
188 | + cd "$srcdir/${pkgname}" |
189 | + |
190 | + mkdir -p ${pkgdir}/opt/${pkgname} |
191 | + cp -rf * ${pkgdir}/opt/${pkgname} |
192 | + make PREFIX=/usr DESTDIR="${pkgdir}" install |
193 | + install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" |
194 | +} |
195 | diff --git a/arg.h b/arg.h |
196 | new file mode 100644 |
197 | index 0000000..dd4bb03 |
198 | --- /dev/null |
199 | +++ b/arg.h |
200 | @@ -0,0 +1,46 @@ |
201 | + |
202 | + |
203 | +#ifndef ARG_H__ |
204 | +#define ARG_H__ |
205 | + |
206 | +extern char *argv0; |
207 | + |
208 | +/* use main(int argc, char *argv[]) */ |
209 | +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ |
210 | + argv[0] && argv[0][0] == '-'\ |
211 | + && argv[0][1];\ |
212 | + argc--, argv++) {\ |
213 | + char argc_;\ |
214 | + char **argv_;\ |
215 | + int brk_;\ |
216 | + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ |
217 | + argv++;\ |
218 | + argc--;\ |
219 | + break;\ |
220 | + }\ |
221 | + for (brk_ = 0, argv[0]++, argv_ = argv;\ |
222 | + argv[0][0] && !brk_;\ |
223 | + argv[0]++) {\ |
224 | + if (argv_ != argv)\ |
225 | + break;\ |
226 | + argc_ = argv[0][0];\ |
227 | + switch (argc_) |
228 | + |
229 | +#define ARGEND }\ |
230 | + } |
231 | + |
232 | +#define ARGC() argc_ |
233 | + |
234 | +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ |
235 | + ((x), abort(), (char *)0) :\ |
236 | + (brk_ = 1, (argv[0][1] != '\0')?\ |
237 | + (&argv[0][1]) :\ |
238 | + (argc--, argv++, argv[0]))) |
239 | + |
240 | +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ |
241 | + (char *)0 :\ |
242 | + (brk_ = 1, (argv[0][1] != '\0')?\ |
243 | + (&argv[0][1]) :\ |
244 | + (argc--, argv++, argv[0]))) |
245 | + |
246 | +#endif |
247 | diff --git a/autolinux-dmenu-archive-0.2.2.tar.gz.sig b/autolinux-dmenu-archive-0.2.2.tar.gz.sig |
248 | new file mode 100644 |
249 | index 0000000..20f04d7 |
250 | Binary files /dev/null and b/autolinux-dmenu-archive-0.2.2.tar.gz.sig differ |
251 | diff --git a/config.h b/config.h |
252 | new file mode 100644 |
253 | index 0000000..cfa568f |
254 | --- /dev/null |
255 | +++ b/config.h |
256 | @@ -0,0 +1,45 @@ |
257 | + |
258 | +/* #------------------------------# */ |
259 | +/* # ## DMENU ## # */ |
260 | +/* #------------------------------# */ |
261 | + |
262 | + |
263 | +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ |
264 | +static int centered = 0; /* -c option; centers dmenu on screen */ |
265 | +static int min_width = 500; /* minimum width when centered */ |
266 | +static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */ |
267 | + |
268 | + |
269 | +/* -fn option overrides fonts[0]; default X11 font or font set */ |
270 | + |
271 | +static const char *fonts[] = { |
272 | + "Hack:pixelsize=11:antialias=true:autohint=true", |
273 | + "JoyPixels:pixelsize=8:antialias=true:autohint=true" |
274 | +}; |
275 | +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ |
276 | +static const char *colors[SchemeLast][2] = { |
277 | + |
278 | + [SchemeNorm] = { "#eee8d5", "#002b36" }, |
279 | + [SchemeSel] = { "#ffffff", "#d33682" }, |
280 | + [SchemeSelHighlight] = { "#2aa198", "#000000" }, |
281 | + [SchemeNormHighlight] = { "#2aa198", "#000000" }, |
282 | + [SchemeOut] = { "#ffffff", "#268bd2" }, |
283 | + [SchemeMid] = { "#eee8d5", "#002b36" }, |
284 | +}; |
285 | + |
286 | + |
287 | +/* -l and -g options; controls number of lines and columns in grid if > 0 */ |
288 | + |
289 | +static unsigned int lines = 0; |
290 | +static unsigned int lineheight = 22; /* -h option; minimum height of a menu line */ |
291 | +static unsigned int columns = 0; |
292 | + |
293 | +/* Characters not considered part of a word while deleting words |
294 | + * for example: " /?\"&[]" |
295 | + */ |
296 | +static const char worddelimiters[] = " "; |
297 | + |
298 | + |
299 | +/* Size of the window border */ |
300 | + |
301 | +static unsigned int border_width = 0; /* -bw option; to add border width */ |
302 | diff --git a/config.mk b/config.mk |
303 | new file mode 100644 |
304 | index 0000000..db908f0 |
305 | --- /dev/null |
306 | +++ b/config.mk |
307 | @@ -0,0 +1,31 @@ |
308 | +# dmenu version |
309 | +VERSION = 5.0 |
310 | + |
311 | +# paths |
312 | +PREFIX = /usr |
313 | +MANPREFIX = $(PREFIX)/share/man |
314 | + |
315 | +X11INC = /usr/X11R6/include |
316 | +X11LIB = /usr/X11R6/lib |
317 | + |
318 | +# Xinerama, comment if you don't want it |
319 | +XINERAMALIBS = -lXinerama |
320 | +XINERAMAFLAGS = -DXINERAMA |
321 | + |
322 | +# freetype |
323 | +FREETYPELIBS = -lfontconfig -lXft |
324 | +FREETYPEINC = /usr/include/freetype2 |
325 | +# OpenBSD (uncomment) |
326 | +#FREETYPEINC = $(X11INC)/freetype2 |
327 | + |
328 | +# includes and libs |
329 | +INCS = -I$(X11INC) -I$(FREETYPEINC) |
330 | +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm |
331 | + |
332 | +# flags |
333 | +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) |
334 | +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) |
335 | +LDFLAGS = $(LIBS) |
336 | + |
337 | +# compiler and linker |
338 | +CC = cc |
339 | diff --git a/dmenu.1 b/dmenu.1 |
340 | new file mode 100644 |
341 | index 0000000..addc2e9 |
342 | --- /dev/null |
343 | +++ b/dmenu.1 |
344 | @@ -0,0 +1,225 @@ |
345 | +.TH DMENU 1 dmenu\-VERSION |
346 | +.SH NAME |
347 | +dmenu \- dynamic menu |
348 | +.SH SYNOPSIS |
349 | +.B dmenu |
350 | +.RB [ \-bfiv ] |
351 | +.RB [ \-g |
352 | +.IR columns ] |
353 | +.RB [ \-l |
354 | +.IR lines ] |
355 | +.RB [ \-m |
356 | +.IR monitor ] |
357 | +.RB [ \-p |
358 | +.IR prompt ] |
359 | +.RB [ \-fn |
360 | +.IR font ] |
361 | +.RB [ \-nb |
362 | +.IR color ] |
363 | +.RB [ \-nf |
364 | +.IR color ] |
365 | +.RB [ \-sb |
366 | +.IR color ] |
367 | +.RB [ \-sf |
368 | +.IR color ] |
369 | +.RB [ \-nhb |
370 | +.IR color ] |
371 | +.RB [ \-nhf |
372 | +.IR color ] |
373 | +.RB [ \-shb |
374 | +.IR color ] |
375 | +.RB [ \-shf |
376 | +.IR color ] |
377 | +.RB [ \-w |
378 | +.IR windowid ] |
379 | +.P |
380 | +.BR dmenu_run " ..." |
381 | +.SH DESCRIPTION |
382 | +.B dmenu |
383 | +is a dynamic menu for X, which reads a list of newline\-separated items from |
384 | +stdin. When the user selects an item and presses Return, their choice is printed |
385 | +to stdout and dmenu terminates. Entering text will narrow the items to those |
386 | +matching the tokens in the input. |
387 | +.P |
388 | +.B dmenu_run |
389 | +is a script used by |
390 | +.IR dwm (1) |
391 | +which lists programs in the user's $PATH and runs the result in their $SHELL. |
392 | +.SH OPTIONS |
393 | +.TP |
394 | +.B \-b |
395 | +dmenu appears at the bottom of the screen. |
396 | +.TP |
397 | +.B \-c |
398 | +dmenu appears centered on the screen. |
399 | +.TP |
400 | +.B \-f |
401 | +dmenu grabs the keyboard before reading stdin if not reading from a tty. This |
402 | +is faster, but will lock up X until stdin reaches end\-of\-file. |
403 | +.TP |
404 | +.B \-i |
405 | +dmenu matches menu items case insensitively. |
406 | +.TP |
407 | +.BI \-g " columns" |
408 | +dmenu lists items in a grid with the given number of columns. |
409 | +.TP |
410 | +.BI \-l " lines" |
411 | +dmenu lists items in a grid with the given number of lines. |
412 | +.TP |
413 | +.BI \-h " height" |
414 | +dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. |
415 | +.TP |
416 | +.BI \-m " monitor" |
417 | +dmenu is displayed on the monitor number supplied. Monitor numbers are starting |
418 | +from 0. |
419 | +.TP |
420 | +.BI \-p " prompt" |
421 | +defines the prompt to be displayed to the left of the input field. |
422 | +.TP |
423 | +.BI \-fn " font" |
424 | +defines the font or font set used. |
425 | +.TP |
426 | +.BI \-nb " color" |
427 | +defines the normal background color. |
428 | +.IR #RGB , |
429 | +.IR #RRGGBB , |
430 | +and X color names are supported. |
431 | +.TP |
432 | +.BI \-nf " color" |
433 | +defines the normal foreground color. |
434 | +.TP |
435 | +.BI \-sb " color" |
436 | +defines the selected background color. |
437 | +.TP |
438 | +.BI \-sf " color" |
439 | +defines the selected foreground color. |
440 | +.TP |
441 | +.BI \-nhb " color" |
442 | +defines the normal highlight background color. |
443 | +.TP |
444 | +.BI \-nhf " color" |
445 | +defines the normal highlight foreground color. |
446 | +.TP |
447 | +.BI \-shb " color" |
448 | +defines the selected highlight background color. |
449 | +.TP |
450 | +.BI \-shf " color" |
451 | +defines the selected highlight foreground color. |
452 | +.TP |
453 | +.B \-v |
454 | +prints version information to stdout, then exits. |
455 | +.TP |
456 | +.BI \-w " windowid" |
457 | +embed into windowid. |
458 | +.SH USAGE |
459 | +dmenu is completely controlled by the keyboard. Items are selected using the |
460 | +arrow keys, page up, page down, home, and end. |
461 | +.TP |
462 | +.B Tab |
463 | +Copy the selected item to the input field. |
464 | +.TP |
465 | +.B Return |
466 | +Confirm selection. Prints the selected item to stdout and exits, returning |
467 | +success. |
468 | +.TP |
469 | +.B Ctrl-Return |
470 | +Confirm selection. Prints the selected item to stdout and continues. |
471 | +.TP |
472 | +.B Shift\-Return |
473 | +Confirm input. Prints the input text to stdout and exits, returning success. |
474 | +.TP |
475 | +.B Escape |
476 | +Exit without selecting an item, returning failure. |
477 | +.TP |
478 | +.B Ctrl-Left |
479 | +Move cursor to the start of the current word |
480 | +.TP |
481 | +.B Ctrl-Right |
482 | +Move cursor to the end of the current word |
483 | +.TP |
484 | +.B C\-a |
485 | +Home |
486 | +.TP |
487 | +.B C\-b |
488 | +Left |
489 | +.TP |
490 | +.B C\-c |
491 | +Escape |
492 | +.TP |
493 | +.B C\-d |
494 | +Delete |
495 | +.TP |
496 | +.B C\-e |
497 | +End |
498 | +.TP |
499 | +.B C\-f |
500 | +Right |
501 | +.TP |
502 | +.B C\-g |
503 | +Escape |
504 | +.TP |
505 | +.B C\-h |
506 | +Backspace |
507 | +.TP |
508 | +.B C\-i |
509 | +Tab |
510 | +.TP |
511 | +.B C\-j |
512 | +Return |
513 | +.TP |
514 | +.B C\-J |
515 | +Shift-Return |
516 | +.TP |
517 | +.B C\-k |
518 | +Delete line right |
519 | +.TP |
520 | +.B C\-m |
521 | +Return |
522 | +.TP |
523 | +.B C\-M |
524 | +Shift-Return |
525 | +.TP |
526 | +.B C\-n |
527 | +Down |
528 | +.TP |
529 | +.B C\-p |
530 | +Up |
531 | +.TP |
532 | +.B C\-u |
533 | +Delete line left |
534 | +.TP |
535 | +.B C\-w |
536 | +Delete word left |
537 | +.TP |
538 | +.B C\-y |
539 | +Paste from primary X selection |
540 | +.TP |
541 | +.B C\-Y |
542 | +Paste from X clipboard |
543 | +.TP |
544 | +.B M\-b |
545 | +Move cursor to the start of the current word |
546 | +.TP |
547 | +.B M\-f |
548 | +Move cursor to the end of the current word |
549 | +.TP |
550 | +.B M\-g |
551 | +Home |
552 | +.TP |
553 | +.B M\-G |
554 | +End |
555 | +.TP |
556 | +.B M\-h |
557 | +Up |
558 | +.TP |
559 | +.B M\-j |
560 | +Page down |
561 | +.TP |
562 | +.B M\-k |
563 | +Page up |
564 | +.TP |
565 | +.B M\-l |
566 | +Down |
567 | +.SH SEE ALSO |
568 | +.IR dwm (1), |
569 | +.IR stest (1) |
570 | diff --git a/dmenu.c b/dmenu.c |
571 | new file mode 100644 |
572 | index 0000000..9448959 |
573 | --- /dev/null |
574 | +++ b/dmenu.c |
575 | @@ -0,0 +1,1097 @@ |
576 | +/* See LICENSE file for copyright and license details. */ |
577 | +#include <ctype.h> |
578 | +#include <locale.h> |
579 | +#include <math.h> |
580 | +#include <stdio.h> |
581 | +#include <stdlib.h> |
582 | +#include <string.h> |
583 | +#include <strings.h> |
584 | +#include <time.h> |
585 | +#include <unistd.h> |
586 | + |
587 | +#include <X11/Xlib.h> |
588 | +#include <X11/Xatom.h> |
589 | +#include <X11/Xutil.h> |
590 | +#ifdef XINERAMA |
591 | +#include <X11/extensions/Xinerama.h> |
592 | +#endif |
593 | +#include <X11/Xft/Xft.h> |
594 | + |
595 | +#include "drw.h" |
596 | +#include "util.h" |
597 | + |
598 | +/* macros */ |
599 | +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ |
600 | + && MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) |
601 | +#define LENGTH(X) (sizeof X / sizeof X[0]) |
602 | +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) |
603 | +#define NUMBERSMAXDIGITS 100 |
604 | +#define NUMBERSBUFSIZE (NUMBERSMAXDIGITS * 2) + 1 |
605 | + |
606 | +/* enums */ |
607 | +enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, |
608 | + SchemeOut, SchemeMid, SchemeLast }; /* color schemes */ |
609 | + |
610 | + |
611 | +struct item { |
612 | + char *text; |
613 | + struct item *left, *right; |
614 | + int out; |
615 | + double distance; |
616 | +}; |
617 | + |
618 | +static char numbers[NUMBERSBUFSIZE] = ""; |
619 | +static char text[BUFSIZ] = ""; |
620 | +static char *embed; |
621 | +static int bh, mw, mh; |
622 | +static int inputw = 0, promptw; |
623 | +static int lrpad; /* sum of left and right padding */ |
624 | +static size_t cursor; |
625 | +static struct item *items = NULL; |
626 | +static struct item *matches, *matchend; |
627 | +static struct item *prev, *curr, *next, *sel; |
628 | +static int mon = -1, screen; |
629 | + |
630 | +static Atom clip, utf8; |
631 | +static Display *dpy; |
632 | +static Window root, parentwin, win; |
633 | +static XIC xic; |
634 | + |
635 | +static Drw *drw; |
636 | +static Clr *scheme[SchemeLast]; |
637 | + |
638 | +#include "config.h" |
639 | + |
640 | +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; |
641 | +static char *(*fstrstr)(const char *, const char *) = strstr; |
642 | + |
643 | +static void |
644 | +appenditem(struct item *item, struct item **list, struct item **last) |
645 | +{ |
646 | + if (*last) |
647 | + (*last)->right = item; |
648 | + else |
649 | + *list = item; |
650 | + |
651 | + item->left = *last; |
652 | + item->right = NULL; |
653 | + *last = item; |
654 | +} |
655 | + |
656 | +static void |
657 | +calcoffsets(void) |
658 | +{ |
659 | + int i, n; |
660 | + |
661 | + if (lines > 0) |
662 | + n = lines * columns * bh; |
663 | + else |
664 | + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); |
665 | + /* calculate which items will begin the next page and previous page */ |
666 | + for (i = 0, next = curr; next; next = next->right) |
667 | + if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) |
668 | + break; |
669 | + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) |
670 | + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) |
671 | + break; |
672 | +} |
673 | + |
674 | +static int |
675 | +max_textw(void) |
676 | +{ |
677 | + int len = 0; |
678 | + for (struct item *item = items; item && item->text; item++) |
679 | + len = MAX(TEXTW(item->text), len); |
680 | + return len; |
681 | +} |
682 | + |
683 | +static void |
684 | +cleanup(void) |
685 | +{ |
686 | + size_t i; |
687 | + |
688 | + XUngrabKey(dpy, AnyKey, AnyModifier, root); |
689 | + for (i = 0; i < SchemeLast; i++) |
690 | + free(scheme[i]); |
691 | + drw_free(drw); |
692 | + XSync(dpy, False); |
693 | + XCloseDisplay(dpy); |
694 | +} |
695 | + |
696 | +static char * |
697 | +cistrstr(const char *s, const char *sub) |
698 | +{ |
699 | + size_t len; |
700 | + |
701 | + for (len = strlen(sub); *s; s++) |
702 | + if (!strncasecmp(s, sub, len)) |
703 | + return (char *)s; |
704 | + return NULL; |
705 | +} |
706 | + |
707 | +static void |
708 | +drawhighlights(struct item *item, int x, int y, int maxw) |
709 | +{ |
710 | + int i, indent; |
711 | + char *highlight; |
712 | + char c; |
713 | + |
714 | + if (!(strlen(item->text) && strlen(text))) |
715 | + return; |
716 | + |
717 | + drw_setscheme(drw, scheme[item == sel |
718 | + ? SchemeSelHighlight |
719 | + : SchemeNormHighlight]); |
720 | + for (i = 0, highlight = item->text; *highlight && text[i];) { |
721 | + if (*highlight == text[i]) { |
722 | + /* get indentation */ |
723 | + c = *highlight; |
724 | + *highlight = '\0'; |
725 | + indent = TEXTW(item->text); |
726 | + *highlight = c; |
727 | + |
728 | + /* highlight character */ |
729 | + c = highlight[1]; |
730 | + highlight[1] = '\0'; |
731 | + drw_text( |
732 | + drw, |
733 | + x + indent - (lrpad / 2), |
734 | + y, |
735 | + MIN(maxw - indent, TEXTW(highlight) - lrpad), |
736 | + bh, 0, highlight, 0 |
737 | + ); |
738 | + highlight[1] = c; |
739 | + i++; |
740 | + } |
741 | + highlight++; |
742 | + } |
743 | +} |
744 | + |
745 | + |
746 | +static int |
747 | +drawitem(struct item *item, int x, int y, int w) |
748 | +{ |
749 | + int r; |
750 | + if (item == sel) |
751 | + drw_setscheme(drw, scheme[SchemeSel]); |
752 | + else if (item->left == sel || item->right == sel) |
753 | + drw_setscheme(drw, scheme[SchemeMid]); |
754 | + else if (item->out) |
755 | + drw_setscheme(drw, scheme[SchemeOut]); |
756 | + else |
757 | + drw_setscheme(drw, scheme[SchemeNorm]); |
758 | + |
759 | + r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); |
760 | + drawhighlights(item, x, y, w); |
761 | + return r; |
762 | +} |
763 | + |
764 | +static void |
765 | +recalculatenumbers() |
766 | +{ |
767 | + unsigned int numer = 0, denom = 0; |
768 | + struct item *item; |
769 | + if (matchend) { |
770 | + numer++; |
771 | + for (item = matchend; item && item->left; item = item->left) |
772 | + numer++; |
773 | + } |
774 | + for (item = items; item && item->text; item++) |
775 | + denom++; |
776 | + snprintf(numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom); |
777 | +} |
778 | + |
779 | +static void |
780 | +drawmenu(void) |
781 | +{ |
782 | + unsigned int curpos; |
783 | + struct item *item; |
784 | + int x = 0, y = 0, fh = drw->fonts->h, w; |
785 | + |
786 | + drw_setscheme(drw, scheme[SchemeNorm]); |
787 | + drw_rect(drw, 0, 0, mw, mh, 1, 1); |
788 | + |
789 | + if (prompt && *prompt) { |
790 | + drw_setscheme(drw, scheme[SchemeSel]); |
791 | + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); |
792 | + } |
793 | + /* draw input field */ |
794 | + w = (lines > 0 || !matches) ? mw - x : inputw; |
795 | + drw_setscheme(drw, scheme[SchemeNorm]); |
796 | + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); |
797 | + |
798 | + curpos = TEXTW(text) - TEXTW(&text[cursor]); |
799 | + if ((curpos += lrpad / 2 - 1) < w) { |
800 | + drw_setscheme(drw, scheme[SchemeNorm]); |
801 | + drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); |
802 | + } |
803 | + |
804 | + recalculatenumbers(); |
805 | + if (lines > 0) { |
806 | + /* draw grid */ |
807 | + int i = 0; |
808 | + for (item = curr; item != next; item = item->right, i++) |
809 | + drawitem( |
810 | + item, |
811 | + x + ((i / lines) * ((mw - x) / columns)), |
812 | + y + (((i % lines) + 1) * bh), |
813 | + (mw - x) / columns |
814 | + ); |
815 | + } else if (matches) { |
816 | + /* draw horizontal list */ |
817 | + x += inputw; |
818 | + w = TEXTW("<"); |
819 | + if (curr->left) { |
820 | + drw_setscheme(drw, scheme[SchemeNorm]); |
821 | + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); |
822 | + } |
823 | + x += w; |
824 | + for (item = curr; item != next; item = item->right) |
825 | + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">") - TEXTW(numbers))); |
826 | + if (next) { |
827 | + w = TEXTW(">"); |
828 | + drw_setscheme(drw, scheme[SchemeNorm]); |
829 | + drw_text(drw, mw - w - TEXTW(numbers), 0, w, bh, lrpad / 2, ">", 0); |
830 | + } |
831 | + } |
832 | + drw_setscheme(drw, scheme[SchemeNorm]); |
833 | + drw_text(drw, mw - TEXTW(numbers), 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0); |
834 | + drw_map(drw, win, 0, 0, mw, mh); |
835 | +} |
836 | + |
837 | +static void |
838 | +grabfocus(void) |
839 | +{ |
840 | + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; |
841 | + Window focuswin; |
842 | + int i, revertwin; |
843 | + |
844 | + for (i = 0; i < 100; ++i) { |
845 | + XGetInputFocus(dpy, &focuswin, &revertwin); |
846 | + if (focuswin == win) |
847 | + return; |
848 | + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); |
849 | + nanosleep(&ts, NULL); |
850 | + } |
851 | + die("cannot grab focus"); |
852 | +} |
853 | + |
854 | +static void |
855 | +grabkeyboard(void) |
856 | +{ |
857 | + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; |
858 | + int i; |
859 | + |
860 | + if (embed) |
861 | + return; |
862 | + /* try to grab keyboard, we may have to wait for another process to ungrab */ |
863 | + for (i = 0; i < 1000; i++) { |
864 | + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, |
865 | + GrabModeAsync, CurrentTime) == GrabSuccess) |
866 | + return; |
867 | + nanosleep(&ts, NULL); |
868 | + } |
869 | + die("cannot grab keyboard"); |
870 | +} |
871 | + |
872 | +int |
873 | +compare_distance(const void *a, const void *b) |
874 | +{ |
875 | + struct item *da = *(struct item **) a; |
876 | + struct item *db = *(struct item **) b; |
877 | + |
878 | + if (!db) |
879 | + return 1; |
880 | + if (!da) |
881 | + return -1; |
882 | + |
883 | + return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; |
884 | +} |
885 | + |
886 | +void |
887 | +fuzzymatch(void) |
888 | +{ |
889 | + /* bang - we have so much memory */ |
890 | + struct item *it; |
891 | + struct item **fuzzymatches = NULL; |
892 | + char c; |
893 | + int number_of_matches = 0, i, pidx, sidx, eidx; |
894 | + int text_len = strlen(text), itext_len; |
895 | + |
896 | + matches = matchend = NULL; |
897 | + |
898 | + /* walk through all items */ |
899 | + for (it = items; it && it->text; it++) { |
900 | + if (text_len) { |
901 | + itext_len = strlen(it->text); |
902 | + pidx = 0; /* pointer */ |
903 | + sidx = eidx = -1; /* start of match, end of match */ |
904 | + /* walk through item text */ |
905 | + for (i = 0; i < itext_len && (c = it->text[i]); i++) { |
906 | + /* fuzzy match pattern */ |
907 | + if (!fstrncmp(&text[pidx], &c, 1)) { |
908 | + if(sidx == -1) |
909 | + sidx = i; |
910 | + pidx++; |
911 | + if (pidx == text_len) { |
912 | + eidx = i; |
913 | + break; |
914 | + } |
915 | + } |
916 | + } |
917 | + /* build list of matches */ |
918 | + if (eidx != -1) { |
919 | + /* compute distance */ |
920 | + /* add penalty if match starts late (log(sidx+2)) |
921 | + * add penalty for long a match without many matching characters */ |
922 | + it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len); |
923 | + /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */ |
924 | + appenditem(it, &matches, &matchend); |
925 | + number_of_matches++; |
926 | + } |
927 | + } else { |
928 | + appenditem(it, &matches, &matchend); |
929 | + } |
930 | + } |
931 | + |
932 | + if (number_of_matches) { |
933 | + /* initialize array with matches */ |
934 | + if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) |
935 | + die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*)); |
936 | + for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) { |
937 | + fuzzymatches[i] = it; |
938 | + } |
939 | + /* sort matches according to distance */ |
940 | + qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance); |
941 | + /* rebuild list of matches */ |
942 | + matches = matchend = NULL; |
943 | + for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \ |
944 | + it->text; i++, it = fuzzymatches[i]) { |
945 | + appenditem(it, &matches, &matchend); |
946 | + } |
947 | + free(fuzzymatches); |
948 | + } |
949 | + curr = sel = matches; |
950 | + calcoffsets(); |
951 | +} |
952 | + |
953 | +static void |
954 | +match(void) |
955 | +{ |
956 | + if (fuzzy) { |
957 | + fuzzymatch(); |
958 | + return; |
959 | + } |
960 | + static char **tokv = NULL; |
961 | + static int tokn = 0; |
962 | + |
963 | + char buf[sizeof text], *s; |
964 | + int i, tokc = 0; |
965 | + size_t len, textsize; |
966 | + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; |
967 | + |
968 | + strcpy(buf, text); |
969 | + /* separate input text into tokens to be matched individually */ |
970 | + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) |
971 | + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) |
972 | + die("cannot realloc %u bytes:", tokn * sizeof *tokv); |
973 | + len = tokc ? strlen(tokv[0]) : 0; |
974 | + |
975 | + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; |
976 | + textsize = strlen(text) + 1; |
977 | + for (item = items; item && item->text; item++) { |
978 | + for (i = 0; i < tokc; i++) |
979 | + if (!fstrstr(item->text, tokv[i])) |
980 | + break; |
981 | + if (i != tokc) /* not all tokens match */ |
982 | + continue; |
983 | + /* exact matches go first, then prefixes, then substrings */ |
984 | + if (!tokc || !fstrncmp(text, item->text, textsize)) |
985 | + appenditem(item, &matches, &matchend); |
986 | + else if (!fstrncmp(tokv[0], item->text, len)) |
987 | + appenditem(item, &lprefix, &prefixend); |
988 | + else |
989 | + appenditem(item, &lsubstr, &substrend); |
990 | + } |
991 | + if (lprefix) { |
992 | + if (matches) { |
993 | + matchend->right = lprefix; |
994 | + lprefix->left = matchend; |
995 | + } else |
996 | + matches = lprefix; |
997 | + matchend = prefixend; |
998 | + } |
999 | + if (lsubstr) { |