autolinux-dmenu


Logs | Files | LICENSE | LICENSE | GitLab


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) {