Xinqi Bao's Git

renamed some functions to make it more readable
[dmenu.git] / main.c
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
18 typedef struct Item Item;
19 struct Item {
20 Item *next; /* traverses all items */
21 Item *left, *right; /* traverses items matching current search pattern */
22 char *text;
23 };
24
25 /* static */
26
27 static char text[4096];
28 static int mx, my, mw, mh;
29 static int ret = 0;
30 static int nitem = 0;
31 static unsigned int cmdw = 0;
32 static Bool done = False;
33 static Item *allitems = NULL; /* first of all items */
34 static Item *item = NULL; /* first of pattern matching items */
35 static Item *sel = NULL;
36 static Item *next = NULL;
37 static Item *prev = NULL;
38 static Item *curr = NULL;
39 static Window root;
40 static Window win;
41
42 static void
43 calcoffsets()
44 {
45 unsigned int tw, w;
46
47 if(!curr)
48 return;
49
50 w = cmdw + 2 * SPACE;
51 for(next = curr; next; next=next->right) {
52 tw = textw(next->text);
53 if(tw > mw / 3)
54 tw = mw / 3;
55 w += tw;
56 if(w > mw)
57 break;
58 }
59
60 w = cmdw + 2 * SPACE;
61 for(prev = curr; prev && prev->left; prev=prev->left) {
62 tw = textw(prev->left->text);
63 if(tw > mw / 3)
64 tw = mw / 3;
65 w += tw;
66 if(w > mw)
67 break;
68 }
69 }
70
71 static void
72 drawmenu()
73 {
74 Item *i;
75
76 dc.x = 0;
77 dc.y = 0;
78 dc.w = mw;
79 dc.h = mh;
80 drawtext(NULL, False, False);
81
82 /* print command */
83 if(cmdw && item)
84 dc.w = cmdw;
85 drawtext(text[0] ? text : NULL, False, False);
86 dc.x += cmdw;
87
88 if(curr) {
89 dc.w = SPACE;
90 drawtext((curr && curr->left) ? "<" : NULL, False, False);
91 dc.x += dc.w;
92
93 /* determine maximum items */
94 for(i = curr; i != next; i=i->right) {
95 dc.border = False;
96 dc.w = textw(i->text);
97 if(dc.w > mw / 3)
98 dc.w = mw / 3;
99 drawtext(i->text, sel == i, sel == i);
100 dc.x += dc.w;
101 }
102
103 dc.x = mw - SPACE;
104 dc.w = SPACE;
105 drawtext(next ? ">" : NULL, False, False);
106 }
107 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0);
108 XFlush(dpy);
109 }
110
111 static void
112 match(char *pattern)
113 {
114 unsigned int plen;
115 Item *i, *j;
116
117 if(!pattern)
118 return;
119
120 plen = strlen(pattern);
121 item = j = NULL;
122 nitem = 0;
123
124 for(i = allitems; i; i=i->next)
125 if(!plen || !strncmp(pattern, i->text, plen)) {
126 if(!j)
127 item = i;
128 else
129 j->right = i;
130 i->left = j;
131 i->right = NULL;
132 j = i;
133 nitem++;
134 }
135 for(i = allitems; i; i=i->next)
136 if(plen && strncmp(pattern, i->text, plen)
137 && strstr(i->text, pattern)) {
138 if(!j)
139 item = i;
140 else
141 j->right = i;
142 i->left = j;
143 i->right = NULL;
144 j = i;
145 nitem++;
146 }
147
148 curr = prev = next = sel = item;
149 calcoffsets();
150 }
151
152 static void
153 kpress(XKeyEvent * e)
154 {
155 char buf[32];
156 int num, prev_nitem;
157 unsigned int i, len;
158 KeySym ksym;
159
160 len = strlen(text);
161 buf[0] = 0;
162 num = XLookupString(e, buf, sizeof(buf), &ksym, 0);
163
164 if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
165 || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
166 || IsPrivateKeypadKey(ksym))
167 return;
168
169 /* first check if a control mask is omitted */
170 if(e->state & ControlMask) {
171 switch (ksym) {
172 default: /* ignore other control sequences */
173 return;
174 break;
175 case XK_h:
176 ksym = XK_BackSpace;
177 break;
178 case XK_U:
179 case XK_u:
180 text[0] = 0;
181 match(text);
182 drawmenu();
183 return;
184 break;
185 case XK_bracketleft:
186 ksym = XK_Escape;
187 break;
188 }
189 }
190 switch(ksym) {
191 case XK_Left:
192 if(!(sel && sel->left))
193 return;
194 sel=sel->left;
195 if(sel->right == curr) {
196 curr = prev;
197 calcoffsets();
198 }
199 break;
200 case XK_Tab:
201 if(!sel)
202 return;
203 strncpy(text, sel->text, sizeof(text));
204 match(text);
205 break;
206 case XK_Right:
207 if(!(sel && sel->right))
208 return;
209 sel=sel->right;
210 if(sel == next) {
211 curr = next;
212 calcoffsets();
213 }
214 break;
215 case XK_Return:
216 if(e->state & ShiftMask) {
217 if(text)
218 fprintf(stdout, "%s", text);
219 }
220 else if(sel)
221 fprintf(stdout, "%s", sel->text);
222 else if(text)
223 fprintf(stdout, "%s", text);
224 fflush(stdout);
225 done = True;
226 break;
227 case XK_Escape:
228 ret = 1;
229 done = True;
230 break;
231 case XK_BackSpace:
232 if((i = len)) {
233 prev_nitem = nitem;
234 do {
235 text[--i] = 0;
236 match(text);
237 } while(i && nitem && prev_nitem == nitem);
238 match(text);
239 }
240 break;
241 default:
242 if(num && !iscntrl((int) buf[0])) {
243 buf[num] = 0;
244 if(len > 0)
245 strncat(text, buf, sizeof(text));
246 else
247 strncpy(text, buf, sizeof(text));
248 match(text);
249 }
250 }
251 drawmenu();
252 }
253
254 static char *
255 readstdin()
256 {
257 static char *maxname = NULL;
258 char *p, buf[1024];
259 unsigned int len = 0, max = 0;
260 Item *i, *new;
261
262 i = 0;
263 while(fgets(buf, sizeof(buf), stdin)) {
264 len = strlen(buf);
265 if (buf[len - 1] == '\n')
266 buf[len - 1] = 0;
267 p = estrdup(buf);
268 if(max < len) {
269 maxname = p;
270 max = len;
271 }
272
273 new = emalloc(sizeof(Item));
274 new->next = new->left = new->right = NULL;
275 new->text = p;
276 if(!i)
277 allitems = new;
278 else
279 i->next = new;
280 i = new;
281 }
282
283 return maxname;
284 }
285
286 /* extern */
287
288 int screen;
289 Display *dpy;
290 DC dc = {0};
291
292 int
293 main(int argc, char *argv[])
294 {
295 char *maxname;
296 XEvent ev;
297 XSetWindowAttributes wa;
298
299 if(argc == 2 && !strncmp("-v", argv[1], 3)) {
300 fputs("dmenu-"VERSION", (C)opyright MMVI Anselm R. Garbe\n", stdout);
301 exit(EXIT_SUCCESS);
302 }
303 else if(argc != 1)
304 eprint("usage: dmenu [-v]\n");
305
306 dpy = XOpenDisplay(0);
307 if(!dpy)
308 eprint("dmenu: cannot open display\n");
309 screen = DefaultScreen(dpy);
310 root = RootWindow(dpy, screen);
311
312 maxname = readstdin();
313
314 /* grab as early as possible, but after reading all items!!! */
315 while(XGrabKeyboard(dpy, root, True, GrabModeAsync,
316 GrabModeAsync, CurrentTime) != GrabSuccess)
317 usleep(1000);
318
319 /* style */
320 dc.bg = getcolor(BGCOLOR);
321 dc.fg = getcolor(FGCOLOR);
322 dc.border = getcolor(BORDERCOLOR);
323 setfont(FONT);
324
325 wa.override_redirect = 1;
326 wa.background_pixmap = ParentRelative;
327 wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask;
328
329 mx = my = 0;
330 mw = DisplayWidth(dpy, screen);
331 mh = dc.font.height + 4;
332
333 win = XCreateWindow(dpy, root, mx, my, mw, mh, 0,
334 DefaultDepth(dpy, screen), CopyFromParent,
335 DefaultVisual(dpy, screen),
336 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
337 XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_xterm));
338
339 /* pixmap */
340 dc.drawable = XCreatePixmap(dpy, root, mw, mh, DefaultDepth(dpy, screen));
341 dc.gc = XCreateGC(dpy, root, 0, 0);
342
343 if(maxname)
344 cmdw = textw(maxname);
345 if(cmdw > mw / 3)
346 cmdw = mw / 3;
347
348 text[0] = 0;
349 match(text);
350 XMapRaised(dpy, win);
351 drawmenu();
352 XSync(dpy, False);
353
354 /* main event loop */
355 while(!done && !XNextEvent(dpy, &ev)) {
356 switch (ev.type) {
357 case KeyPress:
358 kpress(&ev.xkey);
359 break;
360 case Expose:
361 if(ev.xexpose.count == 0)
362 drawmenu();
363 break;
364 default:
365 break;
366 }
367 }
368
369 XUngrabKeyboard(dpy, CurrentTime);
370 XFreePixmap(dpy, dc.drawable);
371 XFreeGC(dpy, dc.gc);
372 XDestroyWindow(dpy, win);
373 XCloseDisplay(dpy);
374
375 return ret;
376 }