Xinqi Bao's Git
1 /* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
2 * (C)opyright MMVI-MMVII Sander van Dijk <a dot h dot vandijk at gmail dot com>
3 * See LICENSE file for license details.
13 #include <sys/select.h>
15 #include <X11/Xutil.h>
16 #include <X11/keysym.h>
18 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
20 typedef struct Item Item
;
22 Item
*next
; /* traverses all items */
23 Item
*left
, *right
; /* traverses items matching current search pattern */
29 static char text
[4096];
30 static char *prompt
= NULL
;
34 static unsigned int cmdw
= 0;
35 static unsigned int promptw
= 0;
36 static unsigned int numlockmask
= 0;
37 static Bool running
= True
;
38 static Item
*allitems
= NULL
; /* first of all items */
39 static Item
*item
= NULL
; /* first of pattern matching items */
40 static Item
*sel
= NULL
;
41 static Item
*next
= NULL
;
42 static Item
*prev
= NULL
;
43 static Item
*curr
= NULL
;
48 textnw(const char *text
, unsigned int len
) {
52 XmbTextExtents(dc
.font
.set
, text
, len
, NULL
, &r
);
55 return XTextWidth(dc
.font
.xfont
, text
, len
);
59 textw(const char *text
) {
60 return textnw(text
, strlen(text
)) + dc
.font
.height
;
69 w
= promptw
+ cmdw
+ 2 * SPACE
;
70 for(next
= curr
; next
; next
=next
->right
) {
71 tw
= textw(next
->text
);
78 w
= promptw
+ cmdw
+ 2 * SPACE
;
79 for(prev
= curr
; prev
&& prev
->left
; prev
=prev
->left
) {
80 tw
= textw(prev
->left
->text
);
90 drawtext(const char *text
, unsigned long col
[ColLast
]) {
93 unsigned int len
, olen
;
95 XRectangle r
= { dc
.x
, dc
.y
, dc
.w
, dc
.h
};
97 XSetForeground(dpy
, dc
.gc
, col
[ColBG
]);
98 XFillRectangles(dpy
, dc
.drawable
, dc
.gc
, &r
, 1);
102 olen
= len
= strlen(text
);
103 if(len
>= sizeof buf
)
104 len
= sizeof buf
- 1;
105 memcpy(buf
, text
, len
);
107 h
= dc
.font
.ascent
+ dc
.font
.descent
;
108 y
= dc
.y
+ (dc
.h
/ 2) - (h
/ 2) + dc
.font
.ascent
;
110 /* shorten text if necessary */
111 while(len
&& (w
= textnw(buf
, len
)) > dc
.w
- h
)
122 return; /* too long */
123 gcv
.foreground
= col
[ColFG
];
125 XChangeGC(dpy
, dc
.gc
, GCForeground
, &gcv
);
126 XmbDrawString(dpy
, dc
.drawable
, dc
.font
.set
, dc
.gc
,
130 gcv
.font
= dc
.font
.xfont
->fid
;
131 XChangeGC(dpy
, dc
.gc
, GCForeground
| GCFont
, &gcv
);
132 XDrawString(dpy
, dc
.drawable
, dc
.gc
, x
, y
, buf
, len
);
144 drawtext(NULL
, dc
.norm
);
148 drawtext(prompt
, dc
.sel
);
155 drawtext(text
[0] ? text
: NULL
, dc
.norm
);
159 drawtext((curr
&& curr
->left
) ? "<" : NULL
, dc
.norm
);
161 /* determine maximum items */
162 for(i
= curr
; i
!= next
; i
=i
->right
) {
163 dc
.w
= textw(i
->text
);
166 drawtext(i
->text
, (sel
== i
) ? dc
.sel
: dc
.norm
);
171 drawtext(next
? ">" : NULL
, dc
.norm
);
173 XCopyArea(dpy
, dc
.drawable
, win
, dc
.gc
, 0, 0, mw
, mh
, 0, 0);
178 getcolor(const char *colstr
) {
179 Colormap cmap
= DefaultColormap(dpy
, screen
);
182 if(!XAllocNamedColor(dpy
, cmap
, colstr
, &color
, &color
))
183 eprint("error, cannot allocate color '%s'\n", colstr
);
188 setfont(const char *fontstr
) {
189 char *def
, **missing
;
194 XFreeFontSet(dpy
, dc
.font
.set
);
195 dc
.font
.set
= XCreateFontSet(dpy
, fontstr
, &missing
, &n
, &def
);
197 XFreeStringList(missing
);
199 XFontSetExtents
*font_extents
;
200 XFontStruct
**xfonts
;
202 dc
.font
.ascent
= dc
.font
.descent
= 0;
203 font_extents
= XExtentsOfFontSet(dc
.font
.set
);
204 n
= XFontsOfFontSet(dc
.font
.set
, &xfonts
, &font_names
);
205 for(i
= 0, dc
.font
.ascent
= 0, dc
.font
.descent
= 0; i
< n
; i
++) {
206 if(dc
.font
.ascent
< (*xfonts
)->ascent
)
207 dc
.font
.ascent
= (*xfonts
)->ascent
;
208 if(dc
.font
.descent
< (*xfonts
)->descent
)
209 dc
.font
.descent
= (*xfonts
)->descent
;
215 XFreeFont(dpy
, dc
.font
.xfont
);
216 dc
.font
.xfont
= NULL
;
217 if(!(dc
.font
.xfont
= XLoadQueryFont(dpy
, fontstr
)))
218 eprint("error, cannot load font: '%s'\n", fontstr
);
219 dc
.font
.ascent
= dc
.font
.xfont
->ascent
;
220 dc
.font
.descent
= dc
.font
.xfont
->descent
;
222 dc
.font
.height
= dc
.font
.ascent
+ dc
.font
.descent
;
226 match(char *pattern
) {
232 plen
= strlen(pattern
);
235 for(i
= allitems
; i
; i
=i
->next
)
236 if(!plen
|| !strncmp(pattern
, i
->text
, plen
)) {
246 for(i
= allitems
; i
; i
=i
->next
)
247 if(plen
&& strncmp(pattern
, i
->text
, plen
)
248 && strstr(i
->text
, pattern
)) {
258 curr
= prev
= next
= sel
= item
;
263 kpress(XKeyEvent
* e
) {
265 int i
, num
, prev_nitem
;
271 num
= XLookupString(e
, buf
, sizeof buf
, &ksym
, 0);
272 if(IsFunctionKey(ksym
) || IsKeypadKey(ksym
)
273 || IsMiscFunctionKey(ksym
) || IsPFKey(ksym
)
274 || IsPrivateKeypadKey(ksym
))
276 /* first check if a control mask is omitted */
277 if(e
->state
& ControlMask
) {
279 default: /* ignore other control sequences */
306 while(i
>= 0 && text
[i
] == ' ')
308 while(i
>= 0 && text
[i
] != ' ')
316 if(CLEANMASK(e
->state
) & Mod1Mask
) {
341 if(num
&& !iscntrl((int) buf
[0])) {
344 strncat(text
, buf
, sizeof text
);
346 strncpy(text
, buf
, sizeof text
);
356 } while(i
&& nitem
&& prev_nitem
== nitem
);
367 while(sel
&& sel
->right
)
381 if(!(sel
&& sel
->left
))
384 if(sel
->right
== curr
) {
402 if((e
->state
& ShiftMask
) && text
)
403 fprintf(stdout
, "%s", text
);
405 fprintf(stdout
, "%s", sel
->text
);
407 fprintf(stdout
, "%s", text
);
412 if(!(sel
&& sel
->right
))
423 strncpy(text
, sel
->text
, sizeof text
);
432 static char *maxname
= NULL
;
434 unsigned int len
= 0, max
= 0;
438 while(fgets(buf
, sizeof buf
, stdin
)) {
440 if (buf
[len
- 1] == '\n')
447 new = emalloc(sizeof(Item
));
448 new->next
= new->left
= new->right
= NULL
;
467 main(int argc
, char *argv
[]) {
471 char *normbg
= NORMBGCOLOR
;
472 char *normfg
= NORMFGCOLOR
;
473 char *selbg
= SELBGCOLOR
;
474 char *selfg
= SELFGCOLOR
;
477 struct timeval timeout
;
480 XModifierKeymap
*modmap
;
481 XSetWindowAttributes wa
;
485 /* command line args */
486 for(i
= 1; i
< argc
; i
++)
487 if(!strncmp(argv
[i
], "-b", 3)) {
490 else if(!strncmp(argv
[i
], "-fn", 4)) {
491 if(++i
< argc
) font
= argv
[i
];
493 else if(!strncmp(argv
[i
], "-nb", 4)) {
494 if(++i
< argc
) normbg
= argv
[i
];
496 else if(!strncmp(argv
[i
], "-nf", 4)) {
497 if(++i
< argc
) normfg
= argv
[i
];
499 else if(!strncmp(argv
[i
], "-p", 3)) {
500 if(++i
< argc
) prompt
= argv
[i
];
502 else if(!strncmp(argv
[i
], "-sb", 4)) {
503 if(++i
< argc
) selbg
= argv
[i
];
505 else if(!strncmp(argv
[i
], "-sf", 4)) {
506 if(++i
< argc
) selfg
= argv
[i
];
508 else if(!strncmp(argv
[i
], "-t", 3)) {
509 if(++i
< argc
) timeout
.tv_sec
= atoi(argv
[i
]);
511 else if(!strncmp(argv
[i
], "-v", 3)) {
512 fputs("dmenu-"VERSION
", (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout
);
516 eprint("usage: dmenu [-b] [-fn <font>] [-nb <color>] [-nf <color>] [-p <prompt>]\n"
517 " [-sb <color>] [-sf <color>] [-t <seconds>] [-v]\n", stdout
);
518 setlocale(LC_CTYPE
, "");
519 dpy
= XOpenDisplay(0);
521 eprint("dmenu: cannot open display\n");
522 screen
= DefaultScreen(dpy
);
523 root
= RootWindow(dpy
, screen
);
525 /* Note, the select() construction allows to grab all keypresses as
526 * early as possible, to not loose them. But if there is no standard
527 * input supplied, we will make sure to exit after MAX_WAIT_STDIN
528 * seconds. This is convenience behavior for rapid typers.
530 while(XGrabKeyboard(dpy
, root
, True
, GrabModeAsync
,
531 GrabModeAsync
, CurrentTime
) != GrabSuccess
)
534 FD_SET(STDIN_FILENO
, &rd
);
535 if(select(ConnectionNumber(dpy
) + 1, &rd
, NULL
, NULL
, &timeout
) < 1)
536 goto UninitializedEnd
;
537 maxname
= readstdin();
538 /* init modifier map */
539 modmap
= XGetModifierMapping(dpy
);
540 for (i
= 0; i
< 8; i
++) {
541 for (j
= 0; j
< modmap
->max_keypermod
; j
++) {
542 if(modmap
->modifiermap
[i
* modmap
->max_keypermod
+ j
] == XKeysymToKeycode(dpy
, XK_Num_Lock
))
543 numlockmask
= (1 << i
);
546 XFreeModifiermap(modmap
);
548 dc
.norm
[ColBG
] = getcolor(normbg
);
549 dc
.norm
[ColFG
] = getcolor(normfg
);
550 dc
.sel
[ColBG
] = getcolor(selbg
);
551 dc
.sel
[ColFG
] = getcolor(selfg
);
554 wa
.override_redirect
= 1;
555 wa
.background_pixmap
= ParentRelative
;
556 wa
.event_mask
= ExposureMask
| ButtonPressMask
| KeyPressMask
;
557 mw
= DisplayWidth(dpy
, screen
);
558 mh
= dc
.font
.height
+ 2;
559 win
= XCreateWindow(dpy
, root
, 0,
560 bottom
? DisplayHeight(dpy
, screen
) - mh
: 0, mw
, mh
, 0,
561 DefaultDepth(dpy
, screen
), CopyFromParent
,
562 DefaultVisual(dpy
, screen
),
563 CWOverrideRedirect
| CWBackPixmap
| CWEventMask
, &wa
);
565 dc
.drawable
= XCreatePixmap(dpy
, root
, mw
, mh
, DefaultDepth(dpy
, screen
));
566 dc
.gc
= XCreateGC(dpy
, root
, 0, 0);
567 XSetLineAttributes(dpy
, dc
.gc
, 1, LineSolid
, CapButt
, JoinMiter
);
569 cmdw
= textw(maxname
);
573 promptw
= textw(prompt
);
578 XMapRaised(dpy
, win
);
582 /* main event loop */
583 while(running
&& !XNextEvent(dpy
, &ev
))
585 default: /* ignore all crap */
591 if(ev
.xexpose
.count
== 0)
598 itm
= allitems
->next
;
599 free(allitems
->text
);
604 XFreeFontSet(dpy
, dc
.font
.set
);
606 XFreeFont(dpy
, dc
.font
.xfont
);
607 XFreePixmap(dpy
, dc
.drawable
);
609 XDestroyWindow(dpy
, win
);
611 XUngrabKeyboard(dpy
, CurrentTime
);