Xinqi Bao's Git
1 /* See LICENSE file for copyright and license details. */
8 #include <X11/keysym.h>
11 #include <X11/Xutil.h>
13 #include <X11/extensions/Xinerama.h>
18 #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
19 #define MIN(a,b) ((a) < (b) ? (a) : (b))
20 #define MAX(a,b) ((a) > (b) ? (a) : (b))
21 #define UTF8_CODEPOINT(c) (((c) & 0xc0) != 0x80)
23 typedef struct Item Item
;
26 Item
*next
; /* traverses all items */
27 Item
*left
, *right
; /* traverses matching items */
30 static void appenditem(Item
*item
, Item
**list
, Item
**last
);
31 static void calcoffsetsh(void);
32 static void calcoffsetsv(void);
33 static char *cistrstr(const char *s
, const char *sub
);
34 static void drawmenu(void);
35 static void drawmenuh(void);
36 static void drawmenuv(void);
37 static void grabkeyboard(void);
38 static void insert(const char *s
, ssize_t n
);
39 static void keypress(XKeyEvent
*e
);
40 static void match(void);
41 static void paste(Atom atom
);
42 static void readstdin(void);
43 static void run(void);
44 static void setup(void);
45 static void usage(void);
48 static char text
[4096];
50 static size_t cursor
= 0;
51 static unsigned int inputw
= 0;
52 static unsigned int lines
= 0;
53 static unsigned int mw
, mh
;
54 static unsigned int promptw
= 0;
55 static unsigned long normcol
[ColLast
];
56 static unsigned long selcol
[ColLast
];
58 static Bool topbar
= True
;
60 static Item
*allitems
, *matches
;
61 static Item
*curr
, *prev
, *next
, *sel
;
62 static Window root
, win
;
64 static int (*fstrncmp
)(const char *, const char *, size_t) = strncmp
;
65 static char *(*fstrstr
)(const char *, const char *) = strstr
;
66 static void (*calcoffsets
)(void) = calcoffsetsh
;
69 appenditem(Item
*item
, Item
**list
, Item
**last
) {
73 (*last
)->right
= item
;
83 w
= promptw
+ inputw
+ textw(&dc
, "<") + textw(&dc
, ">");
84 for(x
= w
, next
= curr
; next
; next
= next
->right
)
85 if((x
+= MIN(textw(&dc
, next
->text
), mw
/ 3)) > mw
)
87 for(x
= w
, prev
= curr
; prev
&& prev
->left
; prev
= prev
->left
)
88 if((x
+= MIN(textw(&dc
, prev
->left
->text
), mw
/ 3)) > mw
)
97 for(i
= 0; i
< lines
&& next
; i
++)
99 mh
= (dc
.font
.height
+ 2) * (i
+ 1);
100 for(i
= 0; i
< lines
&& prev
&& prev
->left
; i
++)
105 cistrstr(const char *s
, const char *sub
) {
111 if((c
= tolower(*sub
++)) != '\0') {
115 if((csub
= *s
++) == '\0')
118 while(tolower(csub
) != c
);
120 while(strncasecmp(s
, sub
, len
) != 0);
132 drawbox(&dc
, normcol
);
133 dc
.h
= dc
.font
.height
+ 2;
134 dc
.y
= topbar
? 0 : mh
- dc
.h
;
138 drawbox(&dc
, selcol
);
139 drawtext(&dc
, prompt
, selcol
);
143 /* print input area */
144 if(matches
&& lines
== 0 && textw(&dc
, text
) <= inputw
)
146 drawtext(&dc
, text
, normcol
);
147 drawline(&dc
, textnw(&dc
, text
, cursor
) + dc
.h
/2 - 2, 2, 1, dc
.h
-4, normcol
);
150 else if(curr
&& (dc
.w
== inputw
|| curr
->next
))
152 commitdraw(&dc
, win
);
160 dc
.w
= textw(&dc
, "<");
162 drawtext(&dc
, "<", normcol
);
164 for(item
= curr
; item
!= next
; item
= item
->right
) {
165 dc
.w
= MIN(textw(&dc
, item
->text
), mw
/ 3);
167 drawbox(&dc
, selcol
);
168 drawtext(&dc
, item
->text
, (item
== sel
) ? selcol
: normcol
);
171 dc
.w
= textw(&dc
, ">");
174 drawtext(&dc
, ">", normcol
);
180 XWindowAttributes wa
;
182 dc
.y
= topbar
? dc
.h
: 0;
184 for(item
= curr
; item
!= next
; item
= item
->right
) {
186 drawbox(&dc
, selcol
);
187 drawtext(&dc
, item
->text
, (item
== sel
) ? selcol
: normcol
);
190 if(!XGetWindowAttributes(dc
.dpy
, win
, &wa
))
191 eprintf("cannot get window attributes\n");
193 XMoveResizeWindow(dc
.dpy
, win
, wa
.x
, wa
.y
+ (topbar
? 0 : wa
.height
- mh
), mw
, mh
);
200 for(i
= 0; i
< 1000; i
++) {
201 if(!XGrabKeyboard(dc
.dpy
, root
, True
, GrabModeAsync
, GrabModeAsync
, CurrentTime
))
205 eprintf("cannot grab keyboard\n");
209 insert(const char *s
, ssize_t n
) {
210 memmove(text
+ cursor
+ n
, text
+ cursor
, sizeof text
- cursor
- n
);
212 memcpy(text
+ cursor
, s
, n
);
218 keypress(XKeyEvent
*e
) {
219 char buf
[sizeof text
];
225 XLookupString(e
, buf
, sizeof buf
, &ksym
, NULL
);
226 if(e
->state
& ControlMask
) {
227 switch(tolower(ksym
)) {
255 case XK_k
: /* delete right */
264 case XK_u
: /* delete left */
265 insert(NULL
, -cursor
);
267 case XK_w
: /* delete word */
271 while(cursor
- n
++ > 0 && text
[cursor
- n
] == ' ');
272 while(cursor
- n
++ > 0 && text
[cursor
- n
] != ' ');
273 insert(NULL
, -(--n
));
275 case XK_y
: /* paste selection */
276 XConvertSelection(dc
.dpy
, XA_PRIMARY
, utf8
, None
, win
, CurrentTime
);
277 /* causes SelectionNotify event */
283 if(!iscntrl((int)*buf
))
284 insert(buf
, MIN(strlen(buf
), sizeof text
- cursor
));
289 for(n
= 1; cursor
- n
> 0 && !UTF8_CODEPOINT(text
[cursor
- n
]); n
++);
295 for(n
= 1; cursor
+ n
< len
&& !UTF8_CODEPOINT(text
[cursor
+ n
]); n
++);
308 while(sel
&& sel
->right
)
318 sel
= curr
= matches
;
322 if(cursor
> 0 && (!sel
|| !sel
->left
|| lines
> 0)) {
323 while(cursor
-- > 0 && !UTF8_CODEPOINT(text
[cursor
]));
329 if(!sel
|| !sel
->left
)
332 if(sel
->right
== curr
) {
351 fputs(((e
->state
& ShiftMask
) || sel
) ? sel
->text
: text
, stdout
);
356 while(cursor
++ < len
&& !UTF8_CODEPOINT(text
[cursor
]));
362 if(!sel
|| !sel
->right
)
373 strncpy(text
, sel
->text
, sizeof text
);
374 cursor
= strlen(text
);
384 Item
*item
, *itemend
, *lexact
, *lprefix
, *lsubstr
, *exactend
, *prefixend
, *substrend
;
387 matches
= lexact
= lprefix
= lsubstr
= itemend
= exactend
= prefixend
= substrend
= NULL
;
388 for(item
= allitems
; item
; item
= item
->next
)
389 if(!fstrncmp(text
, item
->text
, len
+ 1))
390 appenditem(item
, &lexact
, &exactend
);
391 else if(!fstrncmp(text
, item
->text
, len
))
392 appenditem(item
, &lprefix
, &prefixend
);
393 else if(fstrstr(item
->text
, text
))
394 appenditem(item
, &lsubstr
, &substrend
);
401 itemend
->right
= lprefix
;
402 lprefix
->left
= itemend
;
410 itemend
->right
= lsubstr
;
411 lsubstr
->left
= itemend
;
416 curr
= prev
= next
= sel
= matches
;
428 XGetWindowProperty(dc
.dpy
, win
, atom
, 0, sizeof text
- cursor
, True
,
429 utf8
, &da
, &di
, &dl
, &dl
, (unsigned char **)&p
);
430 insert(p
, (q
= strchr(p
, '\n')) ? q
-p
: strlen(p
));
437 char buf
[sizeof text
];
442 for(item
= NULL
; fgets(buf
, sizeof buf
, stdin
); item
= new) {
444 if(buf
[len
-1] == '\n')
446 if(!(new = malloc(sizeof *new)))
447 eprintf("cannot malloc %u bytes\n", sizeof *new);
448 if(!(new->text
= strdup(buf
)))
449 eprintf("cannot strdup %u bytes\n", len
);
450 inputw
= MAX(inputw
, textw(&dc
, new->text
));
451 new->next
= new->left
= new->right
= NULL
;
463 while(!XNextEvent(dc
.dpy
, &ev
))
466 if(ev
.xexpose
.count
== 0)
472 case SelectionNotify
:
473 if(ev
.xselection
.property
!= None
)
474 paste(ev
.xselection
.property
);
476 case VisibilityNotify
:
477 if(ev
.xvisibility
.state
!= VisibilityUnobscured
)
478 XRaiseWindow(dc
.dpy
, win
);
488 XineramaScreenInfo
*info
;
490 XSetWindowAttributes wa
;
492 normcol
[ColBG
] = getcolor(&dc
, normbgcolor
);
493 normcol
[ColFG
] = getcolor(&dc
, normfgcolor
);
494 selcol
[ColBG
] = getcolor(&dc
, selbgcolor
);
495 selcol
[ColFG
] = getcolor(&dc
, selfgcolor
);
497 /* input window geometry */
498 mh
= (dc
.font
.height
+ 2) * (lines
+ 1);
500 if((info
= XineramaQueryScreens(dc
.dpy
, &n
))) {
505 XQueryPointer(dc
.dpy
, root
, &dw
, &dw
, &x
, &y
, &di
, &di
, &du
);
506 for(i
= 0; i
< n
; i
++)
507 if(INRECT(x
, y
, info
[i
].x_org
, info
[i
].y_org
, info
[i
].width
, info
[i
].height
))
510 y
= info
[i
].y_org
+ (topbar
? 0 : info
[i
].height
- mh
);
518 y
= topbar
? 0 : DisplayHeight(dc
.dpy
, screen
) - mh
;
519 mw
= DisplayWidth(dc
.dpy
, screen
);
523 wa
.override_redirect
= True
;
524 wa
.background_pixmap
= ParentRelative
;
525 wa
.event_mask
= ExposureMask
| KeyPressMask
| VisibilityChangeMask
;
526 win
= XCreateWindow(dc
.dpy
, root
, x
, y
, mw
, mh
, 0,
527 DefaultDepth(dc
.dpy
, screen
), CopyFromParent
,
528 DefaultVisual(dc
.dpy
, screen
),
529 CWOverrideRedirect
| CWBackPixmap
| CWEventMask
, &wa
);
534 inputw
= MIN(inputw
, mw
/ 3);
535 utf8
= XInternAtom(dc
.dpy
, "UTF8_STRING", False
);
536 XMapRaised(dc
.dpy
, win
);
541 fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n"
542 " [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr
);
547 main(int argc
, char *argv
[]) {
551 for(i
= 1; i
< argc
; i
++)
553 if(!strcmp(argv
[i
], "-v")) {
554 fputs("dmenu-"VERSION
", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout
);
557 else if(!strcmp(argv
[i
], "-b"))
559 else if(!strcmp(argv
[i
], "-i")) {
560 fstrncmp
= strncasecmp
;
566 else if(!strcmp(argv
[i
], "-l")) {
567 if((lines
= atoi(argv
[++i
])) > 0)
568 calcoffsets
= calcoffsetsv
;
570 else if(!strcmp(argv
[i
], "-p")) {
572 promptw
= MIN(textw(&dc
, prompt
), mw
/5);
574 else if(!strcmp(argv
[i
], "-fn"))
576 else if(!strcmp(argv
[i
], "-nb"))
577 normbgcolor
= argv
[++i
];
578 else if(!strcmp(argv
[i
], "-nf"))
579 normfgcolor
= argv
[++i
];
580 else if(!strcmp(argv
[i
], "-sb"))
581 selbgcolor
= argv
[++i
];
582 else if(!strcmp(argv
[i
], "-sf"))
583 selfgcolor
= argv
[++i
];
587 if(!setlocale(LC_CTYPE
, "") || !XSupportsLocale())
588 fputs("dmenu: warning: no locale support\n", stderr
);
589 if(!(dc
.dpy
= XOpenDisplay(NULL
)))
590 eprintf("cannot open display\n");
591 screen
= DefaultScreen(dc
.dpy
);
592 root
= RootWindow(dc
.dpy
, screen
);
599 return EXIT_FAILURE
; /* should not reach */