Xinqi Bao's Git
54a3a12ca91adff4f06e8d633478283a0e314cec
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 <X11/Xutil.h>
14 #include <X11/keysym.h>
16 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
18 typedef struct Item Item
;
20 Item
*next
; /* traverses all items */
21 Item
*left
, *right
; /* traverses items matching current search pattern */
27 static char text
[4096];
28 static char *prompt
= NULL
;
32 static unsigned int cmdw
= 0;
33 static unsigned int promptw
= 0;
34 static unsigned int numlockmask
= 0;
35 static Bool running
= True
;
36 static Item
*allitems
= NULL
; /* first of all items */
37 static Item
*item
= NULL
; /* first of pattern matching items */
38 static Item
*sel
= NULL
;
39 static Item
*next
= NULL
;
40 static Item
*prev
= NULL
;
41 static Item
*curr
= NULL
;
46 textnw(const char *text
, unsigned int len
) {
50 XmbTextExtents(dc
.font
.set
, text
, len
, NULL
, &r
);
53 return XTextWidth(dc
.font
.xfont
, text
, len
);
57 textw(const char *text
) {
58 return textnw(text
, strlen(text
)) + dc
.font
.height
;
67 w
= promptw
+ cmdw
+ 2 * SPACE
;
68 for(next
= curr
; next
; next
=next
->right
) {
69 tw
= textw(next
->text
);
76 w
= promptw
+ cmdw
+ 2 * SPACE
;
77 for(prev
= curr
; prev
&& prev
->left
; prev
=prev
->left
) {
78 tw
= textw(prev
->left
->text
);
88 drawtext(const char *text
, unsigned long col
[ColLast
]) {
91 unsigned int len
, olen
;
93 XRectangle r
= { dc
.x
, dc
.y
, dc
.w
, dc
.h
};
95 XSetForeground(dpy
, dc
.gc
, col
[ColBG
]);
96 XFillRectangles(dpy
, dc
.drawable
, dc
.gc
, &r
, 1);
100 olen
= len
= strlen(text
);
101 if(len
>= sizeof buf
)
102 len
= sizeof buf
- 1;
103 memcpy(buf
, text
, len
);
105 h
= dc
.font
.ascent
+ dc
.font
.descent
;
106 y
= dc
.y
+ (dc
.h
/ 2) - (h
/ 2) + dc
.font
.ascent
;
108 /* shorten text if necessary */
109 while(len
&& (w
= textnw(buf
, len
)) > dc
.w
- h
)
120 return; /* too long */
121 gcv
.foreground
= col
[ColFG
];
123 XChangeGC(dpy
, dc
.gc
, GCForeground
, &gcv
);
124 XmbDrawString(dpy
, dc
.drawable
, dc
.font
.set
, dc
.gc
,
128 gcv
.font
= dc
.font
.xfont
->fid
;
129 XChangeGC(dpy
, dc
.gc
, GCForeground
| GCFont
, &gcv
);
130 XDrawString(dpy
, dc
.drawable
, dc
.gc
, x
, y
, buf
, len
);
142 drawtext(NULL
, dc
.norm
);
146 drawtext(prompt
, dc
.sel
);
153 drawtext(text
[0] ? text
: NULL
, dc
.norm
);
157 drawtext((curr
&& curr
->left
) ? "<" : NULL
, dc
.norm
);
159 /* determine maximum items */
160 for(i
= curr
; i
!= next
; i
=i
->right
) {
161 dc
.w
= textw(i
->text
);
164 drawtext(i
->text
, (sel
== i
) ? dc
.sel
: dc
.norm
);
169 drawtext(next
? ">" : NULL
, dc
.norm
);
171 XCopyArea(dpy
, dc
.drawable
, win
, dc
.gc
, 0, 0, mw
, mh
, 0, 0);
176 getcolor(const char *colstr
) {
177 Colormap cmap
= DefaultColormap(dpy
, screen
);
180 if(!XAllocNamedColor(dpy
, cmap
, colstr
, &color
, &color
))
181 eprint("error, cannot allocate color '%s'\n", colstr
);
186 setfont(const char *fontstr
) {
187 char *def
, **missing
;
192 XFreeFontSet(dpy
, dc
.font
.set
);
193 dc
.font
.set
= XCreateFontSet(dpy
, fontstr
, &missing
, &n
, &def
);
195 XFreeStringList(missing
);
197 XFontSetExtents
*font_extents
;
198 XFontStruct
**xfonts
;
200 dc
.font
.ascent
= dc
.font
.descent
= 0;
201 font_extents
= XExtentsOfFontSet(dc
.font
.set
);
202 n
= XFontsOfFontSet(dc
.font
.set
, &xfonts
, &font_names
);
203 for(i
= 0, dc
.font
.ascent
= 0, dc
.font
.descent
= 0; i
< n
; i
++) {
204 if(dc
.font
.ascent
< (*xfonts
)->ascent
)
205 dc
.font
.ascent
= (*xfonts
)->ascent
;
206 if(dc
.font
.descent
< (*xfonts
)->descent
)
207 dc
.font
.descent
= (*xfonts
)->descent
;
213 XFreeFont(dpy
, dc
.font
.xfont
);
214 dc
.font
.xfont
= NULL
;
215 if(!(dc
.font
.xfont
= XLoadQueryFont(dpy
, fontstr
)))
216 eprint("error, cannot load font: '%s'\n", fontstr
);
217 dc
.font
.ascent
= dc
.font
.xfont
->ascent
;
218 dc
.font
.descent
= dc
.font
.xfont
->descent
;
220 dc
.font
.height
= dc
.font
.ascent
+ dc
.font
.descent
;
224 match(char *pattern
) {
230 plen
= strlen(pattern
);
233 for(i
= allitems
; i
; i
=i
->next
)
234 if(!plen
|| !strncmp(pattern
, i
->text
, plen
)) {
244 for(i
= allitems
; i
; i
=i
->next
)
245 if(plen
&& strncmp(pattern
, i
->text
, plen
)
246 && strstr(i
->text
, pattern
)) {
256 curr
= prev
= next
= sel
= item
;
261 kpress(XKeyEvent
* e
) {
263 int i
, num
, prev_nitem
;
269 num
= XLookupString(e
, buf
, sizeof buf
, &ksym
, 0);
270 if(IsFunctionKey(ksym
) || IsKeypadKey(ksym
)
271 || IsMiscFunctionKey(ksym
) || IsPFKey(ksym
)
272 || IsPrivateKeypadKey(ksym
))
274 /* first check if a control mask is omitted */
275 if(e
->state
& ControlMask
) {
277 default: /* ignore other control sequences */
304 while(i
>= 0 && text
[i
] == ' ')
306 while(i
>= 0 && text
[i
] != ' ')
314 if(CLEANMASK(e
->state
) & Mod1Mask
) {
339 if(num
&& !iscntrl((int) buf
[0])) {
342 strncat(text
, buf
, sizeof text
);
344 strncpy(text
, buf
, sizeof text
);
354 } while(i
&& nitem
&& prev_nitem
== nitem
);
365 while(sel
&& sel
->right
)
379 if(!(sel
&& sel
->left
))
382 if(sel
->right
== curr
) {
400 if((e
->state
& ShiftMask
) && text
)
401 fprintf(stdout
, "%s", text
);
403 fprintf(stdout
, "%s", sel
->text
);
405 fprintf(stdout
, "%s", text
);
410 if(!(sel
&& sel
->right
))
421 strncpy(text
, sel
->text
, sizeof text
);
430 static char *maxname
= NULL
;
432 unsigned int len
= 0, max
= 0;
436 while(fgets(buf
, sizeof buf
, stdin
)) {
438 if (buf
[len
- 1] == '\n')
445 new = emalloc(sizeof(Item
));
446 new->next
= new->left
= new->right
= NULL
;
460 eprint("usage: dmenu [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n"
461 " [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n");
471 main(int argc
, char *argv
[]) {
475 char *normbg
= NORMBGCOLOR
;
476 char *normfg
= NORMFGCOLOR
;
477 char *selbg
= SELBGCOLOR
;
478 char *selfg
= SELFGCOLOR
;
482 XModifierKeymap
*modmap
;
483 XSetWindowAttributes wa
;
485 if(isatty(STDIN_FILENO
)) {
486 fputs("error: dmenu can't run in an interactive shell\n", stdout
);
489 /* command line args */
490 for(i
= 1; i
< argc
; i
++)
491 if(!strncmp(argv
[i
], "-b", 3)) {
494 else if(!strncmp(argv
[i
], "-fn", 4)) {
495 if(++i
< argc
) font
= argv
[i
];
497 else if(!strncmp(argv
[i
], "-nb", 4)) {
498 if(++i
< argc
) normbg
= argv
[i
];
500 else if(!strncmp(argv
[i
], "-nf", 4)) {
501 if(++i
< argc
) normfg
= argv
[i
];
503 else if(!strncmp(argv
[i
], "-p", 3)) {
504 if(++i
< argc
) prompt
= argv
[i
];
506 else if(!strncmp(argv
[i
], "-sb", 4)) {
507 if(++i
< argc
) selbg
= argv
[i
];
509 else if(!strncmp(argv
[i
], "-sf", 4)) {
510 if(++i
< argc
) selfg
= argv
[i
];
512 else if(!strncmp(argv
[i
], "-v", 3))
513 eprint("dmenu-"VERSION
", (C)opyright MMVI-MMVII Anselm R. Garbe\n");
516 setlocale(LC_CTYPE
, "");
517 dpy
= XOpenDisplay(0);
519 eprint("dmenu: cannot open display\n");
520 screen
= DefaultScreen(dpy
);
521 root
= RootWindow(dpy
, screen
);
522 while(XGrabKeyboard(dpy
, root
, True
, GrabModeAsync
,
523 GrabModeAsync
, CurrentTime
) != GrabSuccess
)
525 maxname
= readstdin();
526 /* init modifier map */
527 modmap
= XGetModifierMapping(dpy
);
528 for (i
= 0; i
< 8; i
++) {
529 for (j
= 0; j
< modmap
->max_keypermod
; j
++) {
530 if(modmap
->modifiermap
[i
* modmap
->max_keypermod
+ j
]
531 == XKeysymToKeycode(dpy
, XK_Num_Lock
))
532 numlockmask
= (1 << i
);
535 XFreeModifiermap(modmap
);
537 dc
.norm
[ColBG
] = getcolor(normbg
);
538 dc
.norm
[ColFG
] = getcolor(normfg
);
539 dc
.sel
[ColBG
] = getcolor(selbg
);
540 dc
.sel
[ColFG
] = getcolor(selfg
);
543 wa
.override_redirect
= 1;
544 wa
.background_pixmap
= ParentRelative
;
545 wa
.event_mask
= ExposureMask
| ButtonPressMask
| KeyPressMask
;
546 mw
= DisplayWidth(dpy
, screen
);
547 mh
= dc
.font
.height
+ 2;
548 win
= XCreateWindow(dpy
, root
, 0,
549 bottom
? DisplayHeight(dpy
, screen
) - mh
: 0, mw
, mh
, 0,
550 DefaultDepth(dpy
, screen
), CopyFromParent
,
551 DefaultVisual(dpy
, screen
),
552 CWOverrideRedirect
| CWBackPixmap
| CWEventMask
, &wa
);
554 dc
.drawable
= XCreatePixmap(dpy
, root
, mw
, mh
, DefaultDepth(dpy
, screen
));
555 dc
.gc
= XCreateGC(dpy
, root
, 0, 0);
556 XSetLineAttributes(dpy
, dc
.gc
, 1, LineSolid
, CapButt
, JoinMiter
);
558 cmdw
= textw(maxname
);
562 promptw
= textw(prompt
);
567 XMapRaised(dpy
, win
);
571 /* main event loop */
572 while(running
&& !XNextEvent(dpy
, &ev
))
574 default: /* ignore all crap */
580 if(ev
.xexpose
.count
== 0)
587 itm
= allitems
->next
;
588 free(allitems
->text
);
593 XFreeFontSet(dpy
, dc
.font
.set
);
595 XFreeFont(dpy
, dc
.font
.xfont
);
596 XFreePixmap(dpy
, dc
.drawable
);
598 XDestroyWindow(dpy
, win
);
599 XUngrabKeyboard(dpy
, CurrentTime
);