aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE21
-rw-r--r--Makefile61
-rw-r--r--README48
-rw-r--r--config.arg.h9
-rw-r--r--config.default.h9
-rw-r--r--config.h9
-rw-r--r--config.mk24
-rw-r--r--dmenu.168
-rw-r--r--dmenu.h58
-rw-r--r--draw.c171
-rw-r--r--main.c426
-rw-r--r--util.c68
12 files changed, 972 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..aa0a3ab
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
1MIT/X Consortium License
2
3(C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
4
5Permission is hereby granted, free of charge, to any person obtaining a
6copy of this software and associated documentation files (the "Software"),
7to deal in the Software without restriction, including without limitation
8the rights to use, copy, modify, merge, publish, distribute, sublicense,
9and/or sell copies of the Software, and to permit persons to whom the
10Software is furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0cfd20d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,61 @@
1# dmenu - dynamic menu
2# (C)opyright MMVI Anselm R. Garbe
3
4include config.mk
5
6SRC = draw.c main.c util.c
7OBJ = ${SRC:.c=.o}
8
9all: options dmenu
10 @echo finished
11
12options:
13 @echo dmenu build options:
14 @echo "CFLAGS = ${CFLAGS}"
15 @echo "LDFLAGS = ${LDFLAGS}"
16 @echo "CC = ${CC}"
17
18.c.o:
19 @echo CC $<
20 @${CC} -c ${CFLAGS} $<
21
22${OBJ}: dmenu.h config.h
23
24config.h:
25 @echo creating $@ from config.default.h
26 @cp config.default.h $@
27
28dmenu: ${OBJ}
29 @echo LD $@
30 @${CC} -o $@ ${OBJ} ${LDFLAGS}
31
32clean:
33 @echo cleaning
34 @rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz
35
36dist: clean
37 @echo creating dist tarball
38 @mkdir -p dmenu-${VERSION}
39 @cp -R LICENSE Makefile README config.mk \
40 dmenu.1 dmenu.h ${SRC} dmenu-${VERSION}
41 @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
42 @gzip dmenu-${VERSION}.tar
43 @rm -rf dmenu-${VERSION}
44
45install: all
46 @echo installing executable file to ${DESTDIR}${PREFIX}/bin
47 @mkdir -p ${DESTDIR}${PREFIX}/bin
48 @cp -f dmenu ${DESTDIR}${PREFIX}/bin
49 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
50 @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
51 @mkdir -p ${DESTDIR}${MANPREFIX}/man1
52 @cp -f dmenu.1 ${DESTDIR}${MANPREFIX}/man1
53 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
54
55uninstall:
56 @echo removing executable file from ${DESTDIR}${PREFIX}/bin
57 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu
58 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
59 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
60
61.PHONY: all options clean dist install uninstall
diff --git a/README b/README
new file mode 100644
index 0000000..5915a27
--- /dev/null
+++ b/README
@@ -0,0 +1,48 @@
1dwm - dynamic window manager
2----------------------------
3dwm is an extremely fast, small, and dynamic X11 window manager.
4
5
6Requirements
7------------
8In order to build dwm you need the Xlib header files.
9
10
11Installation
12------------
13Edit config.mk to match your local setup (dwm is installed into
14the /usr/local namespace by default).
15
16Afterwards enter the following command to build and install dwm (if
17necessary as root):
18
19 make clean install
20
21
22Running dwm
23-----------
24Add the following line to your .xinitrc to start dwm using startx:
25
26 exec dwm
27
28In order to connect dwm to a specific display, make sure that
29the DISPLAY environment variable is set correctly, e.g.:
30
31 DISPLAY=foo.bar:1 exec dwm
32
33(This will start dwm on display :1 of the host foo.bar.)
34
35In order to display status info in the bar, you can do something
36like this in your .xinitrc:
37
38 while true
39 do
40 echo `date` `uptime | sed 's/.*://; s/,//g'`
41 sleep 1
42 done | dwm
43
44
45Configuration
46-------------
47The configuration of dwm is done by creating a custom config.h
48and (re)compiling the source code.
diff --git a/config.arg.h b/config.arg.h
new file mode 100644
index 0000000..008161d
--- /dev/null
+++ b/config.arg.h
@@ -0,0 +1,9 @@
1/*
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * See LICENSE file for license details.
4 */
5
6#define FONT "-*-terminus-medium-*-*-*-13-*-*-*-*-*-iso10646-*"
7#define BGCOLOR "#0a2c2d"
8#define FGCOLOR "#ddeeee"
9#define BORDERCOLOR "#176164"
diff --git a/config.default.h b/config.default.h
new file mode 100644
index 0000000..cf1baea
--- /dev/null
+++ b/config.default.h
@@ -0,0 +1,9 @@
1/*
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * See LICENSE file for license details.
4 */
5
6#define FONT "fixed"
7#define BGCOLOR "#666699"
8#define FGCOLOR "#eeeeee"
9#define BORDERCOLOR "#9999CC"
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..008161d
--- /dev/null
+++ b/config.h
@@ -0,0 +1,9 @@
1/*
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * See LICENSE file for license details.
4 */
5
6#define FONT "-*-terminus-medium-*-*-*-13-*-*-*-*-*-iso10646-*"
7#define BGCOLOR "#0a2c2d"
8#define FGCOLOR "#ddeeee"
9#define BORDERCOLOR "#176164"
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..a4d21ac
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,24 @@
1# dwm version
2VERSION = 0.0
3
4# Customize below to fit your system
5
6# paths
7PREFIX = /usr/local
8MANPREFIX = ${PREFIX}/share/man
9
10X11INC = /usr/X11R6/include
11X11LIB = /usr/X11R6/lib
12
13# includes and libs
14INCS = -I/usr/lib -I${X11INC}
15LIBS = -L/usr/lib -lc -L${X11LIB} -lX11
16
17# flags
18CFLAGS = -O3 ${INCS} -DVERSION=\"${VERSION}\"
19LDFLAGS = ${LIBS}
20#CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\"
21#LDFLAGS = -g ${LIBS}
22
23# compiler
24CC = cc
diff --git a/dmenu.1 b/dmenu.1
new file mode 100644
index 0000000..da69047
--- /dev/null
+++ b/dmenu.1
@@ -0,0 +1,68 @@
1.TH DMENU 1 d-0.0
2.SH NAME
3dmenu \- dynamic menu
4.SH SYNOPSIS
5.B dmenu
6.RB [ \-v ]
7.RB [ \-t
8.IR title ]
9.SH DESCRIPTION
10.SS Overview
11.B dmenu
12is a generic, highly customizable, and efficient menu for the X Window System,
13originally designed for
14.BR dwm (1).
15It supports arbitrary, user defined menu contents.
16.SS Options
17.TP
18.B \-v
19prints version information to stdout, then exits.
20.TP
21.BI \-t " title"
22displays
23.I title
24above the menu.
25.SS Usage
26.B dmenu
27reads a list of newline-separated items from stdin and creates a menu.
28When the user selects an item or enters any text and presses Enter, his choice
29is printed to stdout and
30.B dmenu
31terminates.
32.SS Keyboard Control
33.B dmenu
34is completely controlled by the keyboard. The following keys are recognized:
35.TP 2
36Any printable character
37appends the character to the text in the input field. This works as a filter:
38only items containing this text will be displayed.
39.TP 2
40Left/Right (Control-p/Control-n)
41select the previous/next item.
42.TP 2
43Tab (Control-i)
44copy the selected item to the input field.
45.TP 2
46Enter (Control-j)
47confirm selection and quit (print the selected item to stdout).
48.TP 2
49Shift-Enter (Shift-Control-j)
50confirm selection and quit (print the text in the input field to stdout).
51.TP 2
52Escape (Control-[)
53quit without selecting an item.
54.TP 2
55Backspace (Control-h)
56remove enough characters from the input field to change its filtering effect.
57.TP 2
58Control-u
59remove all characters from the input field.
60.SS Exit codes
61.B dmenu
62returns
63.B 0
64if Enter is pressed on termination,
65.B 1
66if Escape is pressed.
67.SH SEE ALSO
68.BR dwm (1)
diff --git a/dmenu.h b/dmenu.h
new file mode 100644
index 0000000..8d630ba
--- /dev/null
+++ b/dmenu.h
@@ -0,0 +1,58 @@
1/*
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * See LICENSE file for license details.
4 */
5
6#include "config.h"
7#include <X11/Xlib.h>
8#include <X11/Xlocale.h>
9
10typedef struct Brush Brush;
11typedef struct DC DC;
12typedef struct Fnt Fnt;
13
14struct Fnt {
15 XFontStruct *xfont;
16 XFontSet set;
17 int ascent;
18 int descent;
19 int height;
20};
21
22struct DC { /* draw context */
23 int x, y, w, h;
24 unsigned long bg;
25 unsigned long fg;
26 unsigned long border;
27 Drawable drawable;
28 Fnt font;
29 GC gc;
30};
31
32struct Brush {
33 GC gc;
34 Drawable drawable;
35 int x, y, w, h;
36 Fnt font;
37 unsigned long bg;
38 unsigned long fg;
39 unsigned long border;
40};
41
42
43
44/* draw.c */
45extern void draw(Display *dpy, Brush *b, Bool border, const char *text);
46extern void loadcolors(Display *dpy, int screen, Brush *b,
47 const char *bg, const char *fg, const char *bo);
48extern void loadfont(Display *dpy, Fnt *font, const char *fontstr);
49extern unsigned int textnw(Fnt *font, char *text, unsigned int len);
50extern unsigned int textw(Fnt *font, char *text);
51extern unsigned int texth(Fnt *font);
52
53/* util.c */
54extern void *emalloc(unsigned int size);
55extern void *emallocz(unsigned int size);
56extern void eprint(const char *errstr, ...);
57extern char *estrdup(const char *str);
58extern void swap(void **p1, void **p2);
diff --git a/draw.c b/draw.c
new file mode 100644
index 0000000..d747629
--- /dev/null
+++ b/draw.c
@@ -0,0 +1,171 @@
1/*
2 * (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * See LICENSE file for license details.
4 */
5
6#include <stdio.h>
7#include <string.h>
8
9#include "dmenu.h"
10
11static void
12drawborder(Display *dpy, Brush *b)
13{
14 XPoint points[5];
15 XSetLineAttributes(dpy, b->gc, 1, LineSolid, CapButt, JoinMiter);
16 XSetForeground(dpy, b->gc, b->border);
17 points[0].x = b->x;
18 points[0].y = b->y;
19 points[1].x = b->w - 1;
20 points[1].y = 0;
21 points[2].x = 0;
22 points[2].y = b->h - 1;
23 points[3].x = -(b->w - 1);
24 points[3].y = 0;
25 points[4].x = 0;
26 points[4].y = -(b->h - 1);
27 XDrawLines(dpy, b->drawable, b->gc, points, 5, CoordModePrevious);
28}
29
30void
31draw(Display *dpy, Brush *b, Bool border, const char *text)
32{
33 unsigned int x, y, w, h, len;
34 static char buf[256];
35 XGCValues gcv;
36 XRectangle r = { b->x, b->y, b->w, b->h };
37
38 XSetForeground(dpy, b->gc, b->bg);
39 XFillRectangles(dpy, b->drawable, b->gc, &r, 1);
40
41 w = 0;
42 if(border)
43 drawborder(dpy, b);
44
45 if(!text)
46 return;
47
48 len = strlen(text);
49 if(len >= sizeof(buf))
50 len = sizeof(buf) - 1;
51 memcpy(buf, text, len);
52 buf[len] = 0;
53
54 h = b->font.ascent + b->font.descent;
55 y = b->y + (b->h / 2) - (h / 2) + b->font.ascent;
56 x = b->x + (h / 2);
57
58 /* shorten text if necessary */
59 while(len && (w = textnw(&b->font, buf, len)) > b->w - h)
60 buf[--len] = 0;
61
62 if(w > b->w)
63 return; /* too long */
64
65 gcv.foreground = b->fg;
66 gcv.background = b->bg;
67 if(b->font.set) {
68 XChangeGC(dpy, b->gc, GCForeground | GCBackground, &gcv);
69 XmbDrawImageString(dpy, b->drawable, b->font.set, b->gc,
70 x, y, buf, len);
71 }
72 else {
73 gcv.font = b->font.xfont->fid;
74 XChangeGC(dpy, b->gc, GCForeground | GCBackground | GCFont, &gcv);
75 XDrawImageString(dpy, b->drawable, b->gc, x, y, buf, len);
76 }
77}
78
79static unsigned long
80xloadcolors(Display *dpy, Colormap cmap, const char *colstr)
81{
82 XColor color;
83 XAllocNamedColor(dpy, cmap, colstr, &color, &color);
84 return color.pixel;
85}
86
87void
88loadcolors(Display *dpy, int screen, Brush *b,
89 const char *bg, const char *fg, const char *border)
90{
91 Colormap cmap = DefaultColormap(dpy, screen);
92 b->bg = xloadcolors(dpy, cmap, bg);
93 b->fg = xloadcolors(dpy, cmap, fg);
94 b->border = xloadcolors(dpy, cmap, border);
95}
96
97unsigned int
98textnw(Fnt *font, char *text, unsigned int len)
99{
100 XRectangle r;
101 if(font->set) {
102 XmbTextExtents(font->set, text, len, NULL, &r);
103 return r.width;
104 }
105 return XTextWidth(font->xfont, text, len);
106}
107
108unsigned int
109textw(Fnt *font, char *text)
110{
111 return textnw(font, text, strlen(text));
112}
113
114unsigned int
115texth(Fnt *font)
116{
117 return font->height + 4;
118}
119
120void
121loadfont(Display *dpy, Fnt *font, const char *fontstr)
122{
123 char **missing, *def;
124 int n;
125
126 missing = NULL;
127 def = "?";
128 setlocale(LC_ALL, "");
129 if(font->set)
130 XFreeFontSet(dpy, font->set);
131 font->set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
132 if(missing) {
133 while(n--)
134 fprintf(stderr, "missing fontset: %s\n", missing[n]);
135 XFreeStringList(missing);
136 if(font->set) {
137 XFreeFontSet(dpy, font->set);
138 font->set = NULL;
139 }
140 }
141 if(font->set) {
142 XFontSetExtents *font_extents;
143 XFontStruct **xfonts;
144 char **font_names;
145 unsigned int i;
146
147 font->ascent = font->descent = 0;
148 font_extents = XExtentsOfFontSet(font->set);
149 n = XFontsOfFontSet(font->set, &xfonts, &font_names);
150 for(i = 0, font->ascent = 0, font->descent = 0; i < n; i++) {
151 if(font->ascent < (*xfonts)->ascent)
152 font->ascent = (*xfonts)->ascent;
153 if(font->descent < (*xfonts)->descent)
154 font->descent = (*xfonts)->descent;
155 xfonts++;
156 }
157 }
158 else {
159 if(font->xfont)
160 XFreeFont(dpy, font->xfont);
161 font->xfont = NULL;
162 font->xfont = XLoadQueryFont(dpy, fontstr);
163 if (!font->xfont)
164 font->xfont = XLoadQueryFont(dpy, "fixed");
165 if (!font->xfont)
166 eprint("error, cannot load 'fixed' font\n");
167 font->ascent = font->xfont->ascent;
168 font->descent = font->xfont->descent;
169 }
170 font->height = font->ascent + font->descent;
171}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..18263d5
--- /dev/null
+++ b/main.c
@@ -0,0 +1,426 @@
1/*
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com>
4 * See LICENSE file for license details.
5 */
6
7#include "dmenu.h"
8
9#include <ctype.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <string.h>
13#include <unistd.h>
14#include <X11/cursorfont.h>
15#include <X11/Xutil.h>
16#include <X11/keysym.h>
17
18typedef struct Item Item;
19
20struct Item {
21 Item *next; /* traverses all items */
22 Item *left, *right; /* traverses items matching current search pattern */
23 char *text;
24};
25
26static Display *dpy;
27static Window root;
28static Window win;
29static Bool done = False;
30
31static Item *allitem = NULL; /* first of all items */
32static Item *item = NULL; /* first of pattern matching items */
33static Item *sel = NULL;
34static Item *nextoff = NULL;
35static Item *prevoff = NULL;
36static Item *curroff = NULL;
37
38static int screen, mx, my, mw, mh;
39static char *title = NULL;
40static char text[4096];
41static int ret = 0;
42static int nitem = 0;
43static unsigned int cmdw = 0;
44static unsigned int tw = 0;
45static unsigned int cw = 0;
46static const int seek = 30; /* 30px */
47
48static Brush brush = {0};
49
50static void draw_menu();
51static void kpress(XKeyEvent * e);
52
53static char version[] = "dmenu - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n";
54
55static void
56update_offsets()
57{
58 unsigned int tw, w = cmdw + 2 * seek;
59
60 if(!curroff)
61 return;
62
63 for(nextoff = curroff; nextoff; nextoff=nextoff->right) {
64 tw = textw(&brush.font, nextoff->text);
65 if(tw > mw / 3)
66 tw = mw / 3;
67 w += tw + brush.font.height;
68 if(w > mw)
69 break;
70 }
71
72 w = cmdw + 2 * seek;
73 for(prevoff = curroff; prevoff && prevoff->left; prevoff=prevoff->left) {
74 tw = textw(&brush.font, prevoff->left->text);
75 if(tw > mw / 3)
76 tw = mw / 3;
77 w += tw + brush.font.height;
78 if(w > mw)
79 break;
80 }
81}
82
83static void
84update_items(char *pattern)
85{
86 unsigned int plen = strlen(pattern);
87 Item *i, *j;
88
89 if(!pattern)
90 return;
91
92 if(!title || *pattern)
93 cmdw = cw;
94 else
95 cmdw = tw;
96
97 item = j = NULL;
98 nitem = 0;
99
100 for(i = allitem; i; i=i->next)
101 if(!plen || !strncmp(pattern, i->text, plen)) {
102 if(!j)
103 item = i;
104 else
105 j->right = i;
106 i->left = j;
107 i->right = NULL;
108 j = i;
109 nitem++;
110 }
111 for(i = allitem; i; i=i->next)
112 if(plen && strncmp(pattern, i->text, plen)
113 && strstr(i->text, pattern)) {
114 if(!j)
115 item = i;
116 else
117 j->right = i;
118 i->left = j;
119 i->right = NULL;
120 j = i;
121 nitem++;
122 }
123
124 curroff = prevoff = nextoff = sel = item;
125
126 update_offsets();
127}
128
129/* creates brush structs for brush mode drawing */
130static void
131draw_menu()
132{
133 Item *i;
134
135 brush.x = 0;
136 brush.y = 0;
137 brush.w = mw;
138 brush.h = mh;
139 draw(dpy, &brush, False, 0);
140
141 /* print command */
142 if(!title || text[0]) {
143 cmdw = cw;
144 if(cmdw && item)
145 brush.w = cmdw;
146 draw(dpy, &brush, False, text);
147 }
148 else {
149 cmdw = tw;
150 brush.w = cmdw;
151 draw(dpy, &brush, False, title);
152 }
153 brush.x += brush.w;
154
155 if(curroff) {
156 brush.w = seek;
157 draw(dpy, &brush, False, (curroff && curroff->left) ? "<" : 0);
158 brush.x += brush.w;
159
160 /* determine maximum items */
161 for(i = curroff; i != nextoff; i=i->right) {
162 brush.border = False;
163 brush.w = textw(&brush.font, i->text);
164 if(brush.w > mw / 3)
165 brush.w = mw / 3;
166 brush.w += brush.font.height;
167 if(sel == i) {
168 swap((void **)&brush.fg, (void **)&brush.bg);
169 draw(dpy, &brush, True, i->text);
170 swap((void **)&brush.fg, (void **)&brush.bg);
171 }
172 else
173 draw(dpy, &brush, False, i->text);
174 brush.x += brush.w;
175 }
176
177 brush.x = mw - seek;
178 brush.w = seek;
179 draw(dpy, &brush, False, nextoff ? ">" : 0);
180 }
181 XCopyArea(dpy, brush.drawable, win, brush.gc, 0, 0, mw, mh, 0, 0);
182 XFlush(dpy);
183}
184
185static void
186kpress(XKeyEvent * e)
187{
188 KeySym ksym;
189 char buf[32];
190 int num, prev_nitem;
191 unsigned int i, len = strlen(text);
192
193 buf[0] = 0;
194 num = XLookupString(e, buf, sizeof(buf), &ksym, 0);
195
196 if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
197 || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
198 || IsPrivateKeypadKey(ksym))
199 return;
200
201 /* first check if a control mask is omitted */
202 if(e->state & ControlMask) {
203 switch (ksym) {
204 default: /* ignore other control sequences */
205 return;
206 break;
207 case XK_h:
208 ksym = XK_BackSpace;
209 break;
210 case XK_U:
211 case XK_u:
212 text[0] = 0;
213 update_items(text);
214 draw_menu();
215 return;
216 break;
217 case XK_bracketleft:
218 ksym = XK_Escape;
219 break;
220 }
221 }
222 switch(ksym) {
223 case XK_Left:
224 if(!(sel && sel->left))
225 return;
226 sel=sel->left;
227 if(sel->right == curroff) {
228 curroff = prevoff;
229 update_offsets();
230 }
231 break;
232 case XK_Tab:
233 if(!sel)
234 return;
235 strncpy(text, sel->text, sizeof(text));
236 update_items(text);
237 break;
238 case XK_Right:
239 if(!(sel && sel->right))
240 return;
241 sel=sel->right;
242 if(sel == nextoff) {
243 curroff = nextoff;
244 update_offsets();
245 }
246 break;
247 case XK_Return:
248 if(e->state & ShiftMask) {
249 if(text)
250 fprintf(stdout, "%s", text);
251 }
252 else if(sel)
253 fprintf(stdout, "%s", sel->text);
254 else if(text)
255 fprintf(stdout, "%s", text);
256 fflush(stdout);
257 done = True;
258 break;
259 case XK_Escape:
260 ret = 1;
261 done = True;
262 break;
263 case XK_BackSpace:
264 if((i = len)) {
265 prev_nitem = nitem;
266 do {
267 text[--i] = 0;
268 update_items(text);
269 } while(i && nitem && prev_nitem == nitem);
270 update_items(text);
271 }
272 break;
273 default:
274 if(num && !iscntrl((int) buf[0])) {
275 buf[num] = 0;
276 if(len > 0)
277 strncat(text, buf, sizeof(text));
278 else
279 strncpy(text, buf, sizeof(text));
280 update_items(text);
281 }
282 }
283 draw_menu();
284}
285
286static char *
287read_allitems()
288{
289 static char *maxname = NULL;
290 char *p, buf[1024];
291 unsigned int len = 0, max = 0;
292 Item *i, *new;
293
294 i = 0;
295 while(fgets(buf, sizeof(buf), stdin)) {
296 len = strlen(buf);
297 if (buf[len - 1] == '\n')
298 buf[len - 1] = 0;
299 p = estrdup(buf);
300 if(max < len) {
301 maxname = p;
302 max = len;
303 }
304
305 new = emalloc(sizeof(Item));
306 new->next = new->left = new->right = NULL;
307 new->text = p;
308 if(!i)
309 allitem = new;
310 else
311 i->next = new;
312 i = new;
313 }
314
315 return maxname;
316}
317
318int
319main(int argc, char *argv[])
320{
321 int i;
322 XSetWindowAttributes wa;
323 char *maxname;
324 XEvent ev;
325
326 /* command line args */
327 for(i = 1; i < argc; i++) {
328 if (argv[i][0] == '-')
329 switch (argv[i][1]) {
330 case 'v':
331 fprintf(stdout, "%s", version);
332 exit(0);
333 break;
334 case 't':
335 if(++i < argc) {
336 title = argv[i];
337 break;
338 }
339 default:
340 eprint("usage: dmenu [-v] [-t <title>]\n");
341 break;
342 }
343 else
344 eprint("usage: dmenu [-v] [-t <title>]\n");
345 }
346
347 dpy = XOpenDisplay(0);
348 if(!dpy)
349 eprint("dmenu: cannot open dpy\n");
350 screen = DefaultScreen(dpy);
351 root = RootWindow(dpy, screen);
352
353 maxname = read_allitems();
354
355 /* grab as early as possible, but after reading all items!!! */
356 while(XGrabKeyboard(dpy, root, True, GrabModeAsync,
357 GrabModeAsync, CurrentTime) != GrabSuccess)
358 usleep(1000);
359
360 /* style */
361 loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR);
362 loadfont(dpy, &brush.font, FONT);
363
364 wa.override_redirect = 1;
365 wa.background_pixmap = ParentRelative;
366 wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask;
367
368 mx = my = 0;
369 mw = DisplayWidth(dpy, screen);
370 mh = texth(&brush.font);
371
372 win = XCreateWindow(dpy, root, mx, my, mw, mh, 0,
373 DefaultDepth(dpy, screen), CopyFromParent,
374 DefaultVisual(dpy, screen),
375 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
376 XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm));
377 XFlush(dpy);
378
379 /* pixmap */
380 brush.gc = XCreateGC(dpy, root, 0, 0);
381 brush.drawable = XCreatePixmap(dpy, win, mw, mh,
382 DefaultDepth(dpy, screen));
383 XFlush(dpy);
384
385 if(maxname)
386 cw = textw(&brush.font, maxname) + brush.font.height;
387 if(cw > mw / 3)
388 cw = mw / 3;
389
390 if(title) {
391 tw = textw(&brush.font, title) + brush.font.height;
392 if(tw > mw / 3)
393 tw = mw / 3;
394 }
395
396 cmdw = title ? tw : cw;
397
398 text[0] = 0;
399 update_items(text);
400 XMapRaised(dpy, win);
401 draw_menu();
402 XFlush(dpy);
403
404 /* main event loop */
405 while(!done && !XNextEvent(dpy, &ev)) {
406 switch (ev.type) {
407 case KeyPress:
408 kpress(&ev.xkey);
409 break;
410 case Expose:
411 if(ev.xexpose.count == 0)
412 draw_menu();
413 break;
414 default:
415 break;
416 }
417 }
418
419 XUngrabKeyboard(dpy, CurrentTime);
420 XFreePixmap(dpy, brush.drawable);
421 XFreeGC(dpy, brush.gc);
422 XDestroyWindow(dpy, win);
423 XCloseDisplay(dpy);
424
425 return ret;
426}
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..dff7af7
--- /dev/null
+++ b/util.c
@@ -0,0 +1,68 @@
1/*
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
3 * See LICENSE file for license details.
4 */
5#include "dmenu.h"
6#include <stdarg.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/wait.h>
11#include <unistd.h>
12
13/* static */
14
15static void
16bad_malloc(unsigned int size)
17{
18 eprint("fatal: could not malloc() %u bytes\n", size);
19}
20
21/* extern */
22
23void *
24emalloc(unsigned int size)
25{
26 void *res = malloc(size);
27 if(!res)
28 bad_malloc(size);
29 return res;
30}
31
32void *
33emallocz(unsigned int size)
34{
35 void *res = calloc(1, size);
36
37 if(!res)
38 bad_malloc(size);
39 return res;
40}
41
42void
43eprint(const char *errstr, ...)
44{
45 va_list ap;
46
47 va_start(ap, errstr);
48 vfprintf(stderr, errstr, ap);
49 va_end(ap);
50 exit(EXIT_FAILURE);
51}
52
53char *
54estrdup(const char *str)
55{
56 void *res = strdup(str);
57 if(!res)
58 bad_malloc(strlen(str));
59 return res;
60}
61
62void
63swap(void **p1, void **p2)
64{
65 void *tmp = *p1;
66 *p1 = *p2;
67 *p2 = tmp;
68}